Skip to content

Commit

Permalink
Merge pull request #30215 from Microsoft/higherOrderFunctionTypeInfer…
Browse files Browse the repository at this point in the history
…ence

Higher order function type inference
  • Loading branch information
ahejlsberg committed Mar 8, 2019
2 parents a887c6b + a9e924b commit d59e51b
Show file tree
Hide file tree
Showing 13 changed files with 2,555 additions and 126 deletions.
341 changes: 235 additions & 106 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4362,6 +4362,7 @@ namespace ts {
NoDefault = 1 << 0, // Infer unknownType for no inferences (otherwise anyType or emptyObjectType)
AnyDefault = 1 << 1, // Infer anyType for no inferences (otherwise emptyObjectType)
NoFixing = 1 << 2, // Disable type parameter fixing
SkippedGenericFunction = 1 << 3,
}

/**
Expand Down Expand Up @@ -4391,6 +4392,7 @@ namespace ts {
flags: InferenceFlags; // Inference flags
compareTypes: TypeComparer; // Type comparer function
returnMapper?: TypeMapper; // Type mapper for inferences from return types (if any)
inferredTypeParameters?: ReadonlyArray<TypeParameter>;
}

/* @internal */
Expand Down
2 changes: 1 addition & 1 deletion src/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1798,7 +1798,7 @@ namespace ts {
const span = createTextSpanFromBounds(start, end);
const formatContext = formatting.getFormatContext(formatOptions);

return flatMap(deduplicate(errorCodes, equateValues, compareValues), errorCode => {
return flatMap(deduplicate<number>(errorCodes, equateValues, compareValues), errorCode => {
cancellationToken.throwIfCancellationRequested();
return codefix.getFixes({ errorCode, sourceFile, span, program, host, cancellationToken, formatContext, preferences });
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ var e = <K>(x: string, y?: K) => x.length;
>length : number

var r99 = map(e); // should be {}[] for S since a generic lambda is not inferentially typed
>r99 : (a: {}[]) => number[]
>map(e) : (a: {}[]) => number[]
>r99 : <K>(a: string[]) => number[]
>map(e) : <K>(a: string[]) => number[]
>map : <S, T>(f: (x: S) => T) => (a: S[]) => T[]
>e : <K>(x: string, y?: K) => number

Expand All @@ -37,8 +37,8 @@ var e2 = <K>(x: string, y?: K) => x.length;
>length : number

var r100 = map2(e2); // type arg inference should fail for S since a generic lambda is not inferentially typed. Falls back to { length: number }
>r100 : (a: { length: number; }[]) => number[]
>map2(e2) : (a: { length: number; }[]) => number[]
>r100 : <K>(a: string[]) => number[]
>map2(e2) : <K>(a: string[]) => number[]
>map2 : <S extends { length: number; }, T>(f: (x: S) => T) => (a: S[]) => T[]
>e2 : <K>(x: string, y?: K) => number

Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ var id: <T>(x:T) => T;
>x : T

var r23 = dot(id)(id);
>r23 : (_: {}) => {}
>dot(id)(id) : (_: {}) => {}
>r23 : <T>(_: T) => {}
>dot(id)(id) : <T>(_: T) => {}
>dot(id) : <U>(g: (_: U) => {}) => (_: U) => {}
>dot : <T, S>(f: (_: T) => S) => <U>(g: (_: U) => T) => (_: U) => S
>id : <T>(x: T) => T
Expand Down
193 changes: 193 additions & 0 deletions tests/baselines/reference/genericFunctionInference1.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
tests/cases/compiler/genericFunctionInference1.ts(83,14): error TS2345: Argument of type '<a>(value: { key: a; }) => a' is not assignable to parameter of type '(value: Data) => string'.
Type 'number' is not assignable to type 'string'.


==== tests/cases/compiler/genericFunctionInference1.ts (1 errors) ====
declare function pipe<A extends any[], B>(ab: (...args: A) => B): (...args: A) => B;
declare function pipe<A extends any[], B, C>(ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C;
declare function pipe<A extends any[], B, C, D>(ab: (...args: A) => B, bc: (b: B) => C, cd: (c: C) => D): (...args: A) => D;

declare function list<T>(a: T): T[];
declare function box<V>(x: V): { value: V };
declare function foo<T extends { value: T }>(x: T): T;

const f00 = pipe(list);
const f01 = pipe(list, box);
const f02 = pipe(box, list);
const f03 = pipe(x => list(x), box);
const f04 = pipe(list, x => box(x));
const f05 = pipe(x => list(x), x => box(x))
const f06 = pipe(list, pipe(box));
const f07 = pipe(x => list(x), pipe(box));
const f08 = pipe(x => list(x), pipe(x => box(x)));
const f09 = pipe(list, x => x.length);
const f10 = pipe(foo);
const f11 = pipe(foo, foo);

const g00: <T>(x: T) => T[] = pipe(list);
const g01: <T>(x: T) => { value: T[] } = pipe(list, box);
const g02: <T>(x: T) => { value: T }[] = pipe(box, list);
const g03: <T>(x: T) => { value: T[] } = pipe(x => list(x), box);
const g04: <T>(x: T) => { value: T[] } = pipe(list, x => box(x));
const g05: <T>(x: T) => { value: T[] } = pipe(x => list(x), x => box(x))
const g06: <T>(x: T) => { value: T[] } = pipe(list, pipe(box));
const g07: <T>(x: T) => { value: T[] } = pipe(x => list(x), pipe(box));
const g08: <T>(x: T) => { value: T[] } = pipe(x => list(x), pipe(x => box(x)));
const g09: <T>(x: T) => number = pipe(list, x => x.length);
const g10: <T extends { value: T }>(x: T) => T = pipe(foo);
const g12: <T extends { value: T }>(x: T) => T = pipe(foo, foo);

declare function pipe2<A, B, C, D>(ab: (a: A) => B, cd: (c: C) => D): (a: [A, C]) => [B, D];

const f20 = pipe2(list, box);
const f21 = pipe2(box, list);
const f22 = pipe2(list, list);
const f23 = pipe2(box, box);
const f24 = pipe2(f20, f20);
const f25 = pipe2(foo, foo);
const f26 = pipe2(f25, f25);

declare function pipe3<A, B, C>(ab: (a: A) => B, ac: (a: A) => C): (a: A) => [B, C];

const f30 = pipe3(list, box);
const f31 = pipe3(box, list);
const f32 = pipe3(list, list);

declare function pipe4<A, B, C>(funcs: [(a: A) => B, (b: B) => C]): (a: A) => C;

const f40 = pipe4([list, box]);
const f41 = pipe4([box, list]);

declare function pipe5<A, B>(f: (a: A) => B): { f: (a: A) => B };

const f50 = pipe5(list); // No higher order inference

// #417

function mirror<A, B>(f: (a: A) => B): (a: A) => B { return f; }
var identityM = mirror(identity);

var x = 1;
var y = identity(x);
var z = identityM(x);

// #3038

export function keyOf<a>(value: { key: a; }): a {
return value.key;
}
export interface Data {
key: number;
value: Date;
}

var data: Data[] = [];

declare function toKeys<a>(values: a[], toKey: (value: a) => string): string[];

toKeys(data, keyOf); // Error
~~~~~
!!! error TS2345: Argument of type '<a>(value: { key: a; }) => a' is not assignable to parameter of type '(value: Data) => string'.
!!! error TS2345: Type 'number' is not assignable to type 'string'.

// #9366

function flip<a, b, c>(f: (a: a, b: b) => c): (b: b, a: a) => c {
return (b: b, a: a) => f(a, b);
}
function zip<T, U>(x: T, y: U): [T, U] {
return [x, y];
}

var expected: <T, U>(y: U, x: T) => [T, U] = flip(zip);
var actual = flip(zip);

// #9366

const map = <T, U>(transform: (t: T) => U) =>
(arr: T[]) => arr.map(transform)

const identityStr = (t: string) => t;

const arr: string[] = map(identityStr)(['a']);
const arr1: string[] = map(identity)(['a']);

// #9949

function of2<a, b>(one: a, two: b): [a, b] {
return [one, two];
}

const flipped = flip(of2);

// #29904.1

type Component<P> = (props: P) => {};

declare const myHoc1: <P>(C: Component<P>) => Component<P>;
declare const myHoc2: <P>(C: Component<P>) => Component<P>;

declare const MyComponent1: Component<{ foo: 1 }>;

const enhance = pipe(
myHoc1,
myHoc2,
);

const MyComponent2 = enhance(MyComponent1);

// #29904.2

const fn20 = pipe((_a?: {}) => 1);

// #29904.3

type Fn = (n: number) => number;
const fn30: Fn = pipe(
x => x + 1,
x => x * 2,
);

const promise = Promise.resolve(1);
promise.then(
pipe(
x => x + 1,
x => x * 2,
),
);

// #29904.4

declare const getString: () => string;
declare const orUndefined: (name: string) => string | undefined;
declare const identity: <T>(value: T) => T;

const fn40 = pipe(
getString,
string => orUndefined(string),
identity,
);

// #29904.6

declare const getArray: () => string[];
declare const first: <T>(ts: T[]) => T;

const fn60 = pipe(
getArray,
x => x,
first,
);

const fn61 = pipe(
getArray,
identity,
first,
);

const fn62 = pipe(
getArray,
x => x,
x => first(x),
);

Loading

0 comments on commit d59e51b

Please sign in to comment.