-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Contextual generic function types #16305
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
dbf0362
Include SymbolFlags.Function in couldContainTypeVariables check
ahejlsberg 9e613b9
Preserve type parameters in generic contextual pure function types
ahejlsberg 0f6f857
Allow contextual signature to be generic
ahejlsberg 634c75c
Fix fourslash tests
ahejlsberg 8a76939
Accept new baselines
ahejlsberg 61223f2
Add tests
ahejlsberg 34710a3
Fix fourslash tests
ahejlsberg 98a5c9b
Merge branch 'master' into contextualGenericTypes
ahejlsberg 1c967c3
Accept new baselines
ahejlsberg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10256,7 +10256,7 @@ namespace ts { | |
const objectFlags = getObjectFlags(type); | ||
return !!(type.flags & TypeFlags.TypeVariable || | ||
objectFlags & ObjectFlags.Reference && forEach((<TypeReference>type).typeArguments, couldContainTypeVariables) || | ||
objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) || | ||
objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) || | ||
objectFlags & ObjectFlags.Mapped || | ||
type.flags & TypeFlags.UnionOrIntersection && couldUnionOrIntersectionContainTypeVariables(<UnionOrIntersectionType>type)); | ||
} | ||
|
@@ -13059,13 +13059,13 @@ namespace ts { | |
return node ? node.contextualMapper : identityMapper; | ||
} | ||
|
||
// If the given type is an object or union type, if that type has a single signature, and if | ||
// that signature is non-generic, return the signature. Otherwise return undefined. | ||
function getNonGenericSignature(type: Type, node: FunctionExpression | ArrowFunction | MethodDeclaration): Signature { | ||
// If the given type is an object or union type with a single signature, and if that signature has at | ||
// least as many parameters as the given function, return the signature. Otherwise return undefined. | ||
function getContextualCallSignature(type: Type, node: FunctionExpression | ArrowFunction | MethodDeclaration): Signature { | ||
const signatures = getSignaturesOfStructuredType(type, SignatureKind.Call); | ||
if (signatures.length === 1) { | ||
const signature = signatures[0]; | ||
if (!signature.typeParameters && !isAritySmaller(signature, node)) { | ||
if (!isAritySmaller(signature, node)) { | ||
return signature; | ||
} | ||
} | ||
|
@@ -13116,12 +13116,12 @@ namespace ts { | |
return undefined; | ||
} | ||
if (!(type.flags & TypeFlags.Union)) { | ||
return getNonGenericSignature(type, node); | ||
return getContextualCallSignature(type, node); | ||
} | ||
let signatureList: Signature[]; | ||
const types = (<UnionType>type).types; | ||
for (const current of types) { | ||
const signature = getNonGenericSignature(current, node); | ||
const signature = getContextualCallSignature(current, node); | ||
if (signature) { | ||
if (!signatureList) { | ||
// This signature will contribute to contextual union signature | ||
|
@@ -14981,11 +14981,21 @@ 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 mapper = cloneTypeMapper(getContextualMapper(node)); | ||
const instantiatedType = instantiateType(contextualType, mapper); | ||
const returnType = getReturnTypeOfSignature(signature); | ||
// Inferences made from return types have lower priority than all other inferences. | ||
inferTypes(context.inferences, instantiatedType, returnType, InferencePriority.ReturnType); | ||
const instantiatedType = instantiateType(contextualType, cloneTypeMapper(getContextualMapper(node))); | ||
// If the contextual type is a generic pure function type, 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 be inferred as actual | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. type 'any' |
||
// types from the contextual type. For example: | ||
// declare function arrayMap<T, U>(f: (x: T) => U): (a: T[]) => U[]; | ||
// const boxElements: <A>(a: A[]) => { value: A }[] = arrayMap(value => ({ value })); | ||
// Above, the type of the 'value' parameter is inferred to be 'A'. | ||
const contextualSignature = getSingleCallSignature(instantiatedType); | ||
const inferenceSourceType = contextualSignature && contextualSignature.typeParameters ? | ||
getOrCreateTypeFromSignature(getSignatureInstantiation(contextualSignature, contextualSignature.typeParameters)) : | ||
instantiatedType; | ||
const inferenceTargetType = getReturnTypeOfSignature(signature); | ||
// Inferences made from return types have lower priority than all other inferences. | ||
inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, InferencePriority.ReturnType); | ||
} | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
//// [genericContextualTypes1.ts] | ||
type Box<T> = { value: T }; | ||
|
||
declare function wrap<A, B>(f: (a: A) => B): (a: A) => B; | ||
|
||
declare function compose<A, B, C>(f: (a: A) => B, g: (b: B) => C): (a: A) => C; | ||
|
||
declare function list<T>(a: T): T[]; | ||
|
||
declare function unlist<T>(a: T[]): T; | ||
|
||
declare function box<V>(x: V): Box<V>; | ||
|
||
declare function unbox<W>(x: Box<W>): W; | ||
|
||
declare function map<T, U>(a: T[], f: (x: T) => U): U[]; | ||
|
||
declare function identity<T>(x: T): T; | ||
|
||
declare function zip<A, B>(a: A, b: B): [A, B]; | ||
|
||
declare function flip<X, Y, Z>(f: (x: X, y: Y) => Z): (y: Y, x: X) => Z; | ||
|
||
const f00: <A>(x: A) => A[] = list; | ||
const f01: <A>(x: A) => A[] = x => [x]; | ||
const f02: <A>(x: A) => A[] = wrap(list); | ||
const f03: <A>(x: A) => A[] = wrap(x => [x]); | ||
|
||
const f10: <T>(x: T) => Box<T[]> = compose(a => list(a), b => box(b)); | ||
const f11: <T>(x: T) => Box<T[]> = compose(list, box); | ||
const f12: <T>(x: Box<T[]>) => T = compose(a => unbox(a), b => unlist(b)); | ||
const f13: <T>(x: Box<T[]>) => T = compose(unbox, unlist); | ||
|
||
const arrayMap = <T, U>(f: (x: T) => U) => (a: T[]) => a.map(f); | ||
const arrayFilter = <T>(f: (x: T) => boolean) => (a: T[]) => a.filter(f); | ||
|
||
const f20: (a: string[]) => number[] = arrayMap(x => x.length); | ||
const f21: <A>(a: A[]) => A[][] = arrayMap(x => [x]); | ||
const f22: <A>(a: A[]) => A[] = arrayMap(identity); | ||
const f23: <A>(a: A[]) => Box<A>[] = arrayMap(value => ({ value })); | ||
|
||
const f30: (a: string[]) => string[] = arrayFilter(x => x.length > 10); | ||
const f31: <T extends Box<number>>(a: T[]) => T[] = arrayFilter(x => x.value > 10); | ||
|
||
const f40: <A, B>(b: B, a: A) => [A, B] = flip(zip); | ||
|
||
// Repro from #16293 | ||
|
||
type fn = <A>(a: A) => A; | ||
const fn: fn = a => a; | ||
|
||
|
||
//// [genericContextualTypes1.js] | ||
"use strict"; | ||
var f00 = list; | ||
var f01 = function (x) { return [x]; }; | ||
var f02 = wrap(list); | ||
var f03 = wrap(function (x) { return [x]; }); | ||
var f10 = compose(function (a) { return list(a); }, function (b) { return box(b); }); | ||
var f11 = compose(list, box); | ||
var f12 = compose(function (a) { return unbox(a); }, function (b) { return unlist(b); }); | ||
var f13 = compose(unbox, unlist); | ||
var arrayMap = function (f) { return function (a) { return a.map(f); }; }; | ||
var arrayFilter = function (f) { return function (a) { return a.filter(f); }; }; | ||
var f20 = arrayMap(function (x) { return x.length; }); | ||
var f21 = arrayMap(function (x) { return [x]; }); | ||
var f22 = arrayMap(identity); | ||
var f23 = arrayMap(function (value) { return ({ value: value }); }); | ||
var f30 = arrayFilter(function (x) { return x.length > 10; }); | ||
var f31 = arrayFilter(function (x) { return x.value > 10; }); | ||
var f40 = flip(zip); | ||
var fn = function (a) { return a; }; | ||
|
||
|
||
//// [genericContextualTypes1.d.ts] | ||
declare type Box<T> = { | ||
value: T; | ||
}; | ||
declare function wrap<A, B>(f: (a: A) => B): (a: A) => B; | ||
declare function compose<A, B, C>(f: (a: A) => B, g: (b: B) => C): (a: A) => C; | ||
declare function list<T>(a: T): T[]; | ||
declare function unlist<T>(a: T[]): T; | ||
declare function box<V>(x: V): Box<V>; | ||
declare function unbox<W>(x: Box<W>): W; | ||
declare function map<T, U>(a: T[], f: (x: T) => U): U[]; | ||
declare function identity<T>(x: T): T; | ||
declare function zip<A, B>(a: A, b: B): [A, B]; | ||
declare function flip<X, Y, Z>(f: (x: X, y: Y) => Z): (y: Y, x: X) => Z; | ||
declare const f00: <A>(x: A) => A[]; | ||
declare const f01: <A>(x: A) => A[]; | ||
declare const f02: <A>(x: A) => A[]; | ||
declare const f03: <A>(x: A) => A[]; | ||
declare const f10: <T>(x: T) => Box<T[]>; | ||
declare const f11: <T>(x: T) => Box<T[]>; | ||
declare const f12: <T>(x: Box<T[]>) => T; | ||
declare const f13: <T>(x: Box<T[]>) => T; | ||
declare const arrayMap: <T, U>(f: (x: T) => U) => (a: T[]) => U[]; | ||
declare const arrayFilter: <T>(f: (x: T) => boolean) => (a: T[]) => T[]; | ||
declare const f20: (a: string[]) => number[]; | ||
declare const f21: <A>(a: A[]) => A[][]; | ||
declare const f22: <A>(a: A[]) => A[]; | ||
declare const f23: <A>(a: A[]) => Box<A>[]; | ||
declare const f30: (a: string[]) => string[]; | ||
declare const f31: <T extends Box<number>>(a: T[]) => T[]; | ||
declare const f40: <A, B>(b: B, a: A) => [A, B]; | ||
declare type fn = <A>(a: A) => A; | ||
declare const fn: fn; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is meant by "generic pure function type"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A type that has nothing but a single call signature.