Skip to content

Commit

Permalink
Merge pull request #30114 from Microsoft/contextualGenericRestParameter
Browse files Browse the repository at this point in the history
Improve contextual typing by generic rest parameters
  • Loading branch information
ahejlsberg authored Feb 28, 2019
2 parents 237c33b + 0716b87 commit 7f5052b
Show file tree
Hide file tree
Showing 6 changed files with 311 additions and 32 deletions.
17 changes: 14 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10697,9 +10697,9 @@ namespace ts {
return !!(<InferenceContext>mapper).typeParameters;
}

function cloneTypeMapper(mapper: TypeMapper): TypeMapper {
function cloneTypeMapper(mapper: TypeMapper, extraFlags: InferenceFlags = 0): TypeMapper {
return mapper && isInferenceContext(mapper) ?
createInferenceContext(mapper.typeParameters, mapper.signature, mapper.flags | InferenceFlags.NoDefault, mapper.compareTypes, mapper.inferences) :
createInferenceContext(mapper.typeParameters, mapper.signature, mapper.flags | extraFlags, mapper.compareTypes, mapper.inferences) :
mapper;
}

Expand Down Expand Up @@ -20081,7 +20081,7 @@ namespace ts {
// We clone the contextual mapper to avoid disturbing a resolution in progress for an
// outer call expression. Effectively we just want a snapshot of whatever has been
// inferred for any outer call expression so far.
const instantiatedType = instantiateType(contextualType, cloneTypeMapper(getContextualMapper(node)));
const instantiatedType = instantiateType(contextualType, cloneTypeMapper(getContextualMapper(node), InferenceFlags.NoDefault));
// If the contextual type is a generic function type with a single call signature, we
// instantiate the type with its own type parameters and type arguments. This ensures that
// the type parameters are not erased to type any during type inference such that they can
Expand Down Expand Up @@ -21749,6 +21749,17 @@ namespace ts {
}
}
}
const restType = getEffectiveRestType(context);
if (restType && restType.flags & TypeFlags.TypeParameter) {
// The contextual signature has a generic rest parameter. We first instantiate the contextual
// signature (without fixing type parameters) and assign types to contextually typed parameters.
const instantiatedContext = instantiateSignature(context, cloneTypeMapper(mapper));
assignContextualParameterTypes(signature, instantiatedContext);
// We then infer from a tuple type representing the parameters that correspond to the contextual
// rest parameter.
const restPos = getParameterCount(context) - 1;
inferTypes((<InferenceContext>mapper).inferences, getRestTypeAtPosition(signature, restPos), restType);
}
}

function assignContextualParameterTypes(signature: Signature, context: Signature) {
Expand Down
15 changes: 15 additions & 0 deletions tests/baselines/reference/restTuplesFromContextualTypes.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@ tests/cases/conformance/types/rest/restTuplesFromContextualTypes.ts(56,7): error
!!! error TS2345: Property '0' is missing in type 'any[]' but required in type '[T[0], ...T[number][]]'.
}

declare function f5<T extends any[], U>(f: (...args: T) => U): (...args: T) => U;

let g0 = f5(() => "hello");
let g1 = f5((x, y) => 42);
let g2 = f5((x: number, y) => 42);
let g3 = f5((x: number, y: number) => x + y);
let g4 = f5((...args) => true);

declare function pipe<A extends any[], B, C>(f: (...args: A) => B, g: (x: B) => C): (...args: A) => C;

let g5 = pipe(() => true, b => 42);
let g6 = pipe(x => "hello", s => s.length);
let g7 = pipe((x, y) => 42, x => "" + x);
let g8 = pipe((x: number, y: string) => 42, x => "" + x);

// Repro from #25288

declare var tuple: [number, string];
Expand Down
41 changes: 41 additions & 0 deletions tests/baselines/reference/restTuplesFromContextualTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,21 @@ function f4<T extends any[]>(t: T) {
f((a, b, ...x) => {});
}

declare function f5<T extends any[], U>(f: (...args: T) => U): (...args: T) => U;

let g0 = f5(() => "hello");
let g1 = f5((x, y) => 42);
let g2 = f5((x: number, y) => 42);
let g3 = f5((x: number, y: number) => x + y);
let g4 = f5((...args) => true);

declare function pipe<A extends any[], B, C>(f: (...args: A) => B, g: (x: B) => C): (...args: A) => C;

let g5 = pipe(() => true, b => 42);
let g6 = pipe(x => "hello", s => s.length);
let g7 = pipe((x, y) => 42, x => "" + x);
let g8 = pipe((x: number, y: string) => 42, x => "" + x);

// Repro from #25288

declare var tuple: [number, string];
Expand Down Expand Up @@ -275,6 +290,21 @@ function f4(t) {
}
});
}
var g0 = f5(function () { return "hello"; });
var g1 = f5(function (x, y) { return 42; });
var g2 = f5(function (x, y) { return 42; });
var g3 = f5(function (x, y) { return x + y; });
var g4 = f5(function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return true;
});
var g5 = pipe(function () { return true; }, function (b) { return 42; });
var g6 = pipe(function (x) { return "hello"; }, function (s) { return s.length; });
var g7 = pipe(function (x, y) { return 42; }, function (x) { return "" + x; });
var g8 = pipe(function (x, y) { return 42; }, function (x) { return "" + x; });
(function foo(a, b) { }.apply(void 0, tuple));
(function foo() {
var rest = [];
Expand Down Expand Up @@ -309,6 +339,17 @@ declare function f2(cb: (...args: typeof t2) => void): void;
declare const t3: [boolean, ...string[]];
declare function f3(cb: (x: number, ...args: typeof t3) => void): void;
declare function f4<T extends any[]>(t: T): void;
declare function f5<T extends any[], U>(f: (...args: T) => U): (...args: T) => U;
declare let g0: () => string;
declare let g1: (x: any, y: any) => number;
declare let g2: (x: number, y: any) => number;
declare let g3: (x: number, y: number) => number;
declare let g4: (...args: any[]) => boolean;
declare function pipe<A extends any[], B, C>(f: (...args: A) => B, g: (x: B) => C): (...args: A) => C;
declare let g5: () => number;
declare let g6: (x: any) => number;
declare let g7: (x: any, y: any) => string;
declare let g8: (x: number, y: string) => string;
declare var tuple: [number, string];
declare function take(cb: (a: number, b: string) => void): void;
declare type ArgsUnion = [number, string] | [number, Error];
Expand Down
146 changes: 117 additions & 29 deletions tests/baselines/reference/restTuplesFromContextualTypes.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -238,67 +238,155 @@ function f4<T extends any[]>(t: T) {
>x : Symbol(x, Decl(restTuplesFromContextualTypes.ts, 55, 12))
}

declare function f5<T extends any[], U>(f: (...args: T) => U): (...args: T) => U;
>f5 : Symbol(f5, Decl(restTuplesFromContextualTypes.ts, 56, 1))
>T : Symbol(T, Decl(restTuplesFromContextualTypes.ts, 58, 20))
>U : Symbol(U, Decl(restTuplesFromContextualTypes.ts, 58, 36))
>f : Symbol(f, Decl(restTuplesFromContextualTypes.ts, 58, 40))
>args : Symbol(args, Decl(restTuplesFromContextualTypes.ts, 58, 44))
>T : Symbol(T, Decl(restTuplesFromContextualTypes.ts, 58, 20))
>U : Symbol(U, Decl(restTuplesFromContextualTypes.ts, 58, 36))
>args : Symbol(args, Decl(restTuplesFromContextualTypes.ts, 58, 64))
>T : Symbol(T, Decl(restTuplesFromContextualTypes.ts, 58, 20))
>U : Symbol(U, Decl(restTuplesFromContextualTypes.ts, 58, 36))

let g0 = f5(() => "hello");
>g0 : Symbol(g0, Decl(restTuplesFromContextualTypes.ts, 60, 3))
>f5 : Symbol(f5, Decl(restTuplesFromContextualTypes.ts, 56, 1))

let g1 = f5((x, y) => 42);
>g1 : Symbol(g1, Decl(restTuplesFromContextualTypes.ts, 61, 3))
>f5 : Symbol(f5, Decl(restTuplesFromContextualTypes.ts, 56, 1))
>x : Symbol(x, Decl(restTuplesFromContextualTypes.ts, 61, 13))
>y : Symbol(y, Decl(restTuplesFromContextualTypes.ts, 61, 15))

let g2 = f5((x: number, y) => 42);
>g2 : Symbol(g2, Decl(restTuplesFromContextualTypes.ts, 62, 3))
>f5 : Symbol(f5, Decl(restTuplesFromContextualTypes.ts, 56, 1))
>x : Symbol(x, Decl(restTuplesFromContextualTypes.ts, 62, 13))
>y : Symbol(y, Decl(restTuplesFromContextualTypes.ts, 62, 23))

let g3 = f5((x: number, y: number) => x + y);
>g3 : Symbol(g3, Decl(restTuplesFromContextualTypes.ts, 63, 3))
>f5 : Symbol(f5, Decl(restTuplesFromContextualTypes.ts, 56, 1))
>x : Symbol(x, Decl(restTuplesFromContextualTypes.ts, 63, 13))
>y : Symbol(y, Decl(restTuplesFromContextualTypes.ts, 63, 23))
>x : Symbol(x, Decl(restTuplesFromContextualTypes.ts, 63, 13))
>y : Symbol(y, Decl(restTuplesFromContextualTypes.ts, 63, 23))

let g4 = f5((...args) => true);
>g4 : Symbol(g4, Decl(restTuplesFromContextualTypes.ts, 64, 3))
>f5 : Symbol(f5, Decl(restTuplesFromContextualTypes.ts, 56, 1))
>args : Symbol(args, Decl(restTuplesFromContextualTypes.ts, 64, 13))

declare function pipe<A extends any[], B, C>(f: (...args: A) => B, g: (x: B) => C): (...args: A) => C;
>pipe : Symbol(pipe, Decl(restTuplesFromContextualTypes.ts, 64, 31))
>A : Symbol(A, Decl(restTuplesFromContextualTypes.ts, 66, 22))
>B : Symbol(B, Decl(restTuplesFromContextualTypes.ts, 66, 38))
>C : Symbol(C, Decl(restTuplesFromContextualTypes.ts, 66, 41))
>f : Symbol(f, Decl(restTuplesFromContextualTypes.ts, 66, 45))
>args : Symbol(args, Decl(restTuplesFromContextualTypes.ts, 66, 49))
>A : Symbol(A, Decl(restTuplesFromContextualTypes.ts, 66, 22))
>B : Symbol(B, Decl(restTuplesFromContextualTypes.ts, 66, 38))
>g : Symbol(g, Decl(restTuplesFromContextualTypes.ts, 66, 66))
>x : Symbol(x, Decl(restTuplesFromContextualTypes.ts, 66, 71))
>B : Symbol(B, Decl(restTuplesFromContextualTypes.ts, 66, 38))
>C : Symbol(C, Decl(restTuplesFromContextualTypes.ts, 66, 41))
>args : Symbol(args, Decl(restTuplesFromContextualTypes.ts, 66, 85))
>A : Symbol(A, Decl(restTuplesFromContextualTypes.ts, 66, 22))
>C : Symbol(C, Decl(restTuplesFromContextualTypes.ts, 66, 41))

let g5 = pipe(() => true, b => 42);
>g5 : Symbol(g5, Decl(restTuplesFromContextualTypes.ts, 68, 3))
>pipe : Symbol(pipe, Decl(restTuplesFromContextualTypes.ts, 64, 31))
>b : Symbol(b, Decl(restTuplesFromContextualTypes.ts, 68, 25))

let g6 = pipe(x => "hello", s => s.length);
>g6 : Symbol(g6, Decl(restTuplesFromContextualTypes.ts, 69, 3))
>pipe : Symbol(pipe, Decl(restTuplesFromContextualTypes.ts, 64, 31))
>x : Symbol(x, Decl(restTuplesFromContextualTypes.ts, 69, 14))
>s : Symbol(s, Decl(restTuplesFromContextualTypes.ts, 69, 27))
>s.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>s : Symbol(s, Decl(restTuplesFromContextualTypes.ts, 69, 27))
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))

let g7 = pipe((x, y) => 42, x => "" + x);
>g7 : Symbol(g7, Decl(restTuplesFromContextualTypes.ts, 70, 3))
>pipe : Symbol(pipe, Decl(restTuplesFromContextualTypes.ts, 64, 31))
>x : Symbol(x, Decl(restTuplesFromContextualTypes.ts, 70, 15))
>y : Symbol(y, Decl(restTuplesFromContextualTypes.ts, 70, 17))
>x : Symbol(x, Decl(restTuplesFromContextualTypes.ts, 70, 27))
>x : Symbol(x, Decl(restTuplesFromContextualTypes.ts, 70, 27))

let g8 = pipe((x: number, y: string) => 42, x => "" + x);
>g8 : Symbol(g8, Decl(restTuplesFromContextualTypes.ts, 71, 3))
>pipe : Symbol(pipe, Decl(restTuplesFromContextualTypes.ts, 64, 31))
>x : Symbol(x, Decl(restTuplesFromContextualTypes.ts, 71, 15))
>y : Symbol(y, Decl(restTuplesFromContextualTypes.ts, 71, 25))
>x : Symbol(x, Decl(restTuplesFromContextualTypes.ts, 71, 43))
>x : Symbol(x, Decl(restTuplesFromContextualTypes.ts, 71, 43))

// Repro from #25288

declare var tuple: [number, string];
>tuple : Symbol(tuple, Decl(restTuplesFromContextualTypes.ts, 60, 11))
>tuple : Symbol(tuple, Decl(restTuplesFromContextualTypes.ts, 75, 11))

(function foo(a, b){}(...tuple));
>foo : Symbol(foo, Decl(restTuplesFromContextualTypes.ts, 61, 1))
>a : Symbol(a, Decl(restTuplesFromContextualTypes.ts, 61, 14))
>b : Symbol(b, Decl(restTuplesFromContextualTypes.ts, 61, 16))
>tuple : Symbol(tuple, Decl(restTuplesFromContextualTypes.ts, 60, 11))
>foo : Symbol(foo, Decl(restTuplesFromContextualTypes.ts, 76, 1))
>a : Symbol(a, Decl(restTuplesFromContextualTypes.ts, 76, 14))
>b : Symbol(b, Decl(restTuplesFromContextualTypes.ts, 76, 16))
>tuple : Symbol(tuple, Decl(restTuplesFromContextualTypes.ts, 75, 11))

// Repro from #25289

declare function take(cb: (a: number, b: string) => void): void;
>take : Symbol(take, Decl(restTuplesFromContextualTypes.ts, 61, 33))
>cb : Symbol(cb, Decl(restTuplesFromContextualTypes.ts, 65, 22))
>a : Symbol(a, Decl(restTuplesFromContextualTypes.ts, 65, 27))
>b : Symbol(b, Decl(restTuplesFromContextualTypes.ts, 65, 37))
>take : Symbol(take, Decl(restTuplesFromContextualTypes.ts, 76, 33))
>cb : Symbol(cb, Decl(restTuplesFromContextualTypes.ts, 80, 22))
>a : Symbol(a, Decl(restTuplesFromContextualTypes.ts, 80, 27))
>b : Symbol(b, Decl(restTuplesFromContextualTypes.ts, 80, 37))

(function foo(...rest){}(1, ''));
>foo : Symbol(foo, Decl(restTuplesFromContextualTypes.ts, 67, 1))
>rest : Symbol(rest, Decl(restTuplesFromContextualTypes.ts, 67, 14))
>foo : Symbol(foo, Decl(restTuplesFromContextualTypes.ts, 82, 1))
>rest : Symbol(rest, Decl(restTuplesFromContextualTypes.ts, 82, 14))

take(function(...rest){});
>take : Symbol(take, Decl(restTuplesFromContextualTypes.ts, 61, 33))
>rest : Symbol(rest, Decl(restTuplesFromContextualTypes.ts, 68, 14))
>take : Symbol(take, Decl(restTuplesFromContextualTypes.ts, 76, 33))
>rest : Symbol(rest, Decl(restTuplesFromContextualTypes.ts, 83, 14))

// Repro from #29833

type ArgsUnion = [number, string] | [number, Error];
>ArgsUnion : Symbol(ArgsUnion, Decl(restTuplesFromContextualTypes.ts, 68, 26))
>ArgsUnion : Symbol(ArgsUnion, Decl(restTuplesFromContextualTypes.ts, 83, 26))
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))

type TupleUnionFunc = (...params: ArgsUnion) => number;
>TupleUnionFunc : Symbol(TupleUnionFunc, Decl(restTuplesFromContextualTypes.ts, 72, 52))
>params : Symbol(params, Decl(restTuplesFromContextualTypes.ts, 73, 23))
>ArgsUnion : Symbol(ArgsUnion, Decl(restTuplesFromContextualTypes.ts, 68, 26))
>TupleUnionFunc : Symbol(TupleUnionFunc, Decl(restTuplesFromContextualTypes.ts, 87, 52))
>params : Symbol(params, Decl(restTuplesFromContextualTypes.ts, 88, 23))
>ArgsUnion : Symbol(ArgsUnion, Decl(restTuplesFromContextualTypes.ts, 83, 26))

const funcUnionTupleNoRest: TupleUnionFunc = (num, strOrErr) => {
>funcUnionTupleNoRest : Symbol(funcUnionTupleNoRest, Decl(restTuplesFromContextualTypes.ts, 75, 5))
>TupleUnionFunc : Symbol(TupleUnionFunc, Decl(restTuplesFromContextualTypes.ts, 72, 52))
>num : Symbol(num, Decl(restTuplesFromContextualTypes.ts, 75, 46))
>strOrErr : Symbol(strOrErr, Decl(restTuplesFromContextualTypes.ts, 75, 50))
>funcUnionTupleNoRest : Symbol(funcUnionTupleNoRest, Decl(restTuplesFromContextualTypes.ts, 90, 5))
>TupleUnionFunc : Symbol(TupleUnionFunc, Decl(restTuplesFromContextualTypes.ts, 87, 52))
>num : Symbol(num, Decl(restTuplesFromContextualTypes.ts, 90, 46))
>strOrErr : Symbol(strOrErr, Decl(restTuplesFromContextualTypes.ts, 90, 50))

return num;
>num : Symbol(num, Decl(restTuplesFromContextualTypes.ts, 75, 46))
>num : Symbol(num, Decl(restTuplesFromContextualTypes.ts, 90, 46))

};

const funcUnionTupleRest: TupleUnionFunc = (...params) => {
>funcUnionTupleRest : Symbol(funcUnionTupleRest, Decl(restTuplesFromContextualTypes.ts, 79, 5))
>TupleUnionFunc : Symbol(TupleUnionFunc, Decl(restTuplesFromContextualTypes.ts, 72, 52))
>params : Symbol(params, Decl(restTuplesFromContextualTypes.ts, 79, 44))
>funcUnionTupleRest : Symbol(funcUnionTupleRest, Decl(restTuplesFromContextualTypes.ts, 94, 5))
>TupleUnionFunc : Symbol(TupleUnionFunc, Decl(restTuplesFromContextualTypes.ts, 87, 52))
>params : Symbol(params, Decl(restTuplesFromContextualTypes.ts, 94, 44))

const [num, strOrErr] = params;
>num : Symbol(num, Decl(restTuplesFromContextualTypes.ts, 80, 9))
>strOrErr : Symbol(strOrErr, Decl(restTuplesFromContextualTypes.ts, 80, 13))
>params : Symbol(params, Decl(restTuplesFromContextualTypes.ts, 79, 44))
>num : Symbol(num, Decl(restTuplesFromContextualTypes.ts, 95, 9))
>strOrErr : Symbol(strOrErr, Decl(restTuplesFromContextualTypes.ts, 95, 13))
>params : Symbol(params, Decl(restTuplesFromContextualTypes.ts, 94, 44))

return num;
>num : Symbol(num, Decl(restTuplesFromContextualTypes.ts, 80, 9))
>num : Symbol(num, Decl(restTuplesFromContextualTypes.ts, 95, 9))

};

Loading

0 comments on commit 7f5052b

Please sign in to comment.