Skip to content

Instantiate contextual types for return expressions #32386

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19119,7 +19119,7 @@ namespace ts {

// If the given contextual type contains instantiable types and if a mapper representing
// return type inferences is available, instantiate those types using that mapper.
function instantiateContextualType(contextualType: Type | undefined, node: Expression, contextFlags?: ContextFlags): Type | undefined {
function instantiateContextualType(contextualType: Type | undefined, node: Node, contextFlags?: ContextFlags): Type | undefined {
if (contextualType && maybeTypeOfKind(contextualType, TypeFlags.Instantiable)) {
const inferenceContext = getInferenceContext(node);
// If no inferences have been made, nothing is gained from instantiating as type parameters
Expand Down Expand Up @@ -23436,7 +23436,7 @@ namespace ts {
nextType && isUnitType(nextType)) {
const contextualType = !contextualSignature ? undefined :
contextualSignature === getSignatureFromDeclaration(func) ? isGenerator ? undefined : returnType :
getReturnTypeOfSignature(contextualSignature);
instantiateContextualType(getReturnTypeOfSignature(contextualSignature), func);
if (isGenerator) {
yieldType = getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(yieldType, contextualType, IterationTypeKind.Yield, isAsync);
returnType = getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(returnType, contextualType, IterationTypeKind.Return, isAsync);
Expand Down
60 changes: 60 additions & 0 deletions tests/baselines/reference/instantiateContextualTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,40 @@ declare function passContentsToFunc<T>(outerBox: T, consumer: BoxConsumerFromOut
declare const outerBoxOfString: OuterBox<string>;

passContentsToFunc(outerBoxOfString, box => box.value);

// Repro from #32349

type DooDad = 'SOMETHING' | 'ELSE' ;

class Interesting {
public compiles = () : Promise<DooDad> => {
return Promise.resolve().then(() => {
if (1 < 2) {
return 'SOMETHING';
}
return 'ELSE';
});
};
public doesnt = () : Promise<DooDad> => {
return Promise.resolve().then(() => {
return 'ELSE';
});
};
public slightlyDifferentErrorMessage = () : Promise<DooDad> => {
return Promise.resolve().then(() => {
if (1 < 2) {
return 'SOMETHING';
}
return 'SOMETHING';
});
};
}

// Repro from #32349

declare function invoke<T>(f: () => T): T;

let xx: 0 | 1 | 2 = invoke(() => 1);


//// [instantiateContextualTypes.js]
Expand All @@ -162,3 +196,29 @@ var N1;
createElement2(InferFunctionTypes, [(foo) => "" + foo]);
})(N1 || (N1 = {}));
passContentsToFunc(outerBoxOfString, box => box.value);
class Interesting {
constructor() {
this.compiles = () => {
return Promise.resolve().then(() => {
if (1 < 2) {
return 'SOMETHING';
}
return 'ELSE';
});
};
this.doesnt = () => {
return Promise.resolve().then(() => {
return 'ELSE';
});
};
this.slightlyDifferentErrorMessage = () => {
return Promise.resolve().then(() => {
if (1 < 2) {
return 'SOMETHING';
}
return 'SOMETHING';
});
};
}
}
let xx = invoke(() => 1);
74 changes: 74 additions & 0 deletions tests/baselines/reference/instantiateContextualTypes.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -407,3 +407,77 @@ passContentsToFunc(outerBoxOfString, box => box.value);
>box : Symbol(box, Decl(instantiateContextualTypes.ts, 138, 36))
>value : Symbol(value, Decl(instantiateContextualTypes.ts, 121, 20))

// Repro from #32349

type DooDad = 'SOMETHING' | 'ELSE' ;
>DooDad : Symbol(DooDad, Decl(instantiateContextualTypes.ts, 138, 55))

class Interesting {
>Interesting : Symbol(Interesting, Decl(instantiateContextualTypes.ts, 142, 36))

public compiles = () : Promise<DooDad> => {
>compiles : Symbol(Interesting.compiles, Decl(instantiateContextualTypes.ts, 144, 19))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>DooDad : Symbol(DooDad, Decl(instantiateContextualTypes.ts, 138, 55))

return Promise.resolve().then(() => {
>Promise.resolve().then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))

if (1 < 2) {
return 'SOMETHING';
}
return 'ELSE';
});
};
public doesnt = () : Promise<DooDad> => {
>doesnt : Symbol(Interesting.doesnt, Decl(instantiateContextualTypes.ts, 152, 3))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>DooDad : Symbol(DooDad, Decl(instantiateContextualTypes.ts, 138, 55))

return Promise.resolve().then(() => {
>Promise.resolve().then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))

return 'ELSE';
});
};
public slightlyDifferentErrorMessage = () : Promise<DooDad> => {
>slightlyDifferentErrorMessage : Symbol(Interesting.slightlyDifferentErrorMessage, Decl(instantiateContextualTypes.ts, 157, 3))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>DooDad : Symbol(DooDad, Decl(instantiateContextualTypes.ts, 138, 55))

return Promise.resolve().then(() => {
>Promise.resolve().then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))

if (1 < 2) {
return 'SOMETHING';
}
return 'SOMETHING';
});
};
}

// Repro from #32349

declare function invoke<T>(f: () => T): T;
>invoke : Symbol(invoke, Decl(instantiateContextualTypes.ts, 166, 1))
>T : Symbol(T, Decl(instantiateContextualTypes.ts, 170, 24))
>f : Symbol(f, Decl(instantiateContextualTypes.ts, 170, 27))
>T : Symbol(T, Decl(instantiateContextualTypes.ts, 170, 24))
>T : Symbol(T, Decl(instantiateContextualTypes.ts, 170, 24))

let xx: 0 | 1 | 2 = invoke(() => 1);
>xx : Symbol(xx, Decl(instantiateContextualTypes.ts, 172, 3))
>invoke : Symbol(invoke, Decl(instantiateContextualTypes.ts, 166, 1))

96 changes: 96 additions & 0 deletions tests/baselines/reference/instantiateContextualTypes.types
Original file line number Diff line number Diff line change
Expand Up @@ -326,3 +326,99 @@ passContentsToFunc(outerBoxOfString, box => box.value);
>box : InnerBox<string>
>value : string

// Repro from #32349

type DooDad = 'SOMETHING' | 'ELSE' ;
>DooDad : DooDad

class Interesting {
>Interesting : Interesting

public compiles = () : Promise<DooDad> => {
>compiles : () => Promise<DooDad>
>() : Promise<DooDad> => { return Promise.resolve().then(() => { if (1 < 2) { return 'SOMETHING'; } return 'ELSE'; }); } : () => Promise<DooDad>

return Promise.resolve().then(() => {
>Promise.resolve().then(() => { if (1 < 2) { return 'SOMETHING'; } return 'ELSE'; }) : Promise<DooDad>
>Promise.resolve().then : <TResult1 = void, TResult2 = never>(onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>
>Promise.resolve() : Promise<void>
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
>Promise : PromiseConstructor
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
>then : <TResult1 = void, TResult2 = never>(onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>
>() => { if (1 < 2) { return 'SOMETHING'; } return 'ELSE'; } : () => "SOMETHING" | "ELSE"

if (1 < 2) {
>1 < 2 : boolean
>1 : 1
>2 : 2

return 'SOMETHING';
>'SOMETHING' : "SOMETHING"
}
return 'ELSE';
>'ELSE' : "ELSE"

});
};
public doesnt = () : Promise<DooDad> => {
>doesnt : () => Promise<DooDad>
>() : Promise<DooDad> => { return Promise.resolve().then(() => { return 'ELSE'; }); } : () => Promise<DooDad>

return Promise.resolve().then(() => {
>Promise.resolve().then(() => { return 'ELSE'; }) : Promise<DooDad>
>Promise.resolve().then : <TResult1 = void, TResult2 = never>(onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>
>Promise.resolve() : Promise<void>
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
>Promise : PromiseConstructor
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
>then : <TResult1 = void, TResult2 = never>(onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>
>() => { return 'ELSE'; } : () => "ELSE"

return 'ELSE';
>'ELSE' : "ELSE"

});
};
public slightlyDifferentErrorMessage = () : Promise<DooDad> => {
>slightlyDifferentErrorMessage : () => Promise<DooDad>
>() : Promise<DooDad> => { return Promise.resolve().then(() => { if (1 < 2) { return 'SOMETHING'; } return 'SOMETHING'; }); } : () => Promise<DooDad>

return Promise.resolve().then(() => {
>Promise.resolve().then(() => { if (1 < 2) { return 'SOMETHING'; } return 'SOMETHING'; }) : Promise<DooDad>
>Promise.resolve().then : <TResult1 = void, TResult2 = never>(onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>
>Promise.resolve() : Promise<void>
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
>Promise : PromiseConstructor
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
>then : <TResult1 = void, TResult2 = never>(onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>
>() => { if (1 < 2) { return 'SOMETHING'; } return 'SOMETHING'; } : () => "SOMETHING"

if (1 < 2) {
>1 < 2 : boolean
>1 : 1
>2 : 2

return 'SOMETHING';
>'SOMETHING' : "SOMETHING"
}
return 'SOMETHING';
>'SOMETHING' : "SOMETHING"

});
};
}

// Repro from #32349

declare function invoke<T>(f: () => T): T;
>invoke : <T>(f: () => T) => T
>f : () => T

let xx: 0 | 1 | 2 = invoke(() => 1);
>xx : 0 | 1 | 2
>invoke(() => 1) : 1
>invoke : <T>(f: () => T) => T
>() => 1 : () => 1
>1 : 1

34 changes: 34 additions & 0 deletions tests/cases/compiler/instantiateContextualTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,37 @@ declare function passContentsToFunc<T>(outerBox: T, consumer: BoxConsumerFromOut
declare const outerBoxOfString: OuterBox<string>;

passContentsToFunc(outerBoxOfString, box => box.value);

// Repro from #32349

type DooDad = 'SOMETHING' | 'ELSE' ;

class Interesting {
public compiles = () : Promise<DooDad> => {
return Promise.resolve().then(() => {
if (1 < 2) {
return 'SOMETHING';
}
return 'ELSE';
});
};
public doesnt = () : Promise<DooDad> => {
return Promise.resolve().then(() => {
return 'ELSE';
});
};
public slightlyDifferentErrorMessage = () : Promise<DooDad> => {
return Promise.resolve().then(() => {
if (1 < 2) {
return 'SOMETHING';
}
return 'SOMETHING';
});
};
}

// Repro from #32349

declare function invoke<T>(f: () => T): T;

let xx: 0 | 1 | 2 = invoke(() => 1);