- 
                Notifications
    You must be signed in to change notification settings 
- Fork 13.1k
Contextual generic function types #16305
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
                    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 hidden or 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 hidden or 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 hidden or 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.