Skip to content
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

Attempt at implementing propagated inference for free type parameters (#9366) #24626

Closed
wants to merge 4 commits into from
Closed
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
375 changes: 318 additions & 57 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,7 @@ namespace ts {
/* @internal */ emitNode?: EmitNode; // Associated EmitNode (initialized by transforms)
/* @internal */ contextualType?: Type; // Used to temporarily assign a contextual type during overload resolution
/* @internal */ contextualMapper?: TypeMapper; // Mapper for contextual type
/* @internal */ contextualTypeParameters?: TypeParameter[]; // Type parameters from contextual type. Not temporary like contextualType and contextualMapper.
}

export interface JSDocContainer {
Expand Down Expand Up @@ -1096,6 +1097,9 @@ namespace ts {
kind: SyntaxKind.ThisType;
}

/* @internal */
export type MaybeContextSensitive = Expression | MethodDeclaration | ObjectLiteralElementLike | JsxAttributeLike;

export type FunctionOrConstructorTypeNode = FunctionTypeNode | ConstructorTypeNode;

export interface FunctionTypeNode extends TypeNode, SignatureDeclarationBase {
Expand Down Expand Up @@ -3643,6 +3647,7 @@ namespace ts {
AssignmentsMarked = 0x00400000, // Parameter assignments have been marked
ClassWithConstructorReference = 0x00800000, // Class that contains a binding to its constructor inside of the class body.
ConstructorReferenceInClass = 0x01000000, // Binding to a class constructor inside of the class's body.
ContextReset = 0x02000000, // Contextual types were previously set but have been cleared. This means the node does not need to be added to deferredNodes again.
}

/* @internal */
Expand Down Expand Up @@ -3772,6 +3777,8 @@ namespace ts {
aliasTypeArguments?: Type[]; // Alias type arguments (if any)
/* @internal */
wildcardInstantiation?: Type; // Instantiation with type parameters mapped to wildcard type
/* @internal */
freeTypeParameters?: TypeParameter[];
}

/* @internal */
Expand Down Expand Up @@ -3913,6 +3920,7 @@ namespace ts {
export interface AnonymousType extends ObjectType {
target?: AnonymousType; // Instantiation target
mapper?: TypeMapper; // Instantiation mapper
typeArguments?: Type[];
}

/* @internal */
Expand Down Expand Up @@ -4098,6 +4106,10 @@ namespace ts {
isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison
/* @internal */
instantiations?: Map<Signature>; // Generic signature instantiation cache
/* @internal */
inferenceContext?: InferenceContext;
/* @internal */
isContextuallyTyped?: boolean;
}

export const enum IndexKind {
Expand Down Expand Up @@ -4143,6 +4155,7 @@ namespace ts {
InferUnionTypes = 1 << 0, // Infer union types for disjoint candidates (otherwise unknownType)
NoDefault = 1 << 1, // Infer unknownType for no inferences (otherwise anyType or emptyObjectType)
AnyDefault = 1 << 2, // Infer anyType for no inferences (otherwise emptyObjectType)
AlwaysDefault = 1 << 3, // Infer the default type if there are no candidates or constraints (otherwise no inference is made and type parameter is returned)
}

/**
Expand Down Expand Up @@ -4171,6 +4184,7 @@ namespace ts {
inferences: InferenceInfo[]; // Inferences made for each type parameter
flags: InferenceFlags; // Inference flags
compareTypes: TypeComparer; // Type comparer function
useEmptyObjectForNoInference?: boolean;
}

/* @internal */
Expand Down
5 changes: 2 additions & 3 deletions tests/baselines/reference/conditionalTypes1.js
Original file line number Diff line number Diff line change
Expand Up @@ -499,10 +499,9 @@ declare type T15 = Extract<Options, {
declare function f5<T extends Options, K extends string>(p: K): Extract<T, {
k: K;
}>;
declare let x0: {
declare let x0: Extract<T, {
k: "a";
a: number;
};
}>;
declare type OptionsOfKind<K extends Options["k"]> = Extract<Options, {
k: K;
}>;
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/conditionalTypes1.types
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,8 @@ declare function f5<T extends Options, K extends string>(p: K): Extract<T, { k:
>K : K

let x0 = f5("a"); // { k: "a", a: number }
>x0 : { k: "a"; a: number; }
>f5("a") : { k: "a"; a: number; }
>x0 : Extract<T, { k: "a"; }>
>f5("a") : Extract<T, { k: "a"; }>
>f5 : <T extends Options, K extends string>(p: K) => Extract<T, { k: K; }>
>"a" : "a"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ var use: Overload;
use((req, res) => {});
>use((req, res) => {}) : void
>use : Overload
>(req, res) => {} : (req: any, res: any) => void
>req : any
>res : any
>(req, res) => {} : (req: number, res: number) => void
>req : number
>res : number

interface Overload {
>Overload : Overload
Expand All @@ -33,11 +33,11 @@ app.use((err: any, req, res, next) => { return; });
>app.use : IRouterHandler<MyApp> & IRouterMatcher<MyApp>
>app : MyApp
>use : IRouterHandler<MyApp> & IRouterMatcher<MyApp>
>(err: any, req, res, next) => { return; } : (err: any, req: any, res: any, next: any) => void
>(err: any, req, res, next) => { return; } : (err: any, req: Request, res: Response, next: NextFunction) => void
>err : any
>req : any
>res : any
>next : any
>req : Request
>res : Response
>next : NextFunction


interface MyApp {
Expand Down
10 changes: 5 additions & 5 deletions tests/baselines/reference/genericContextualTypes1.types
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ const f02: <A>(x: A) => A[] = wrap(list);
>x : A
>A : A
>A : A
>wrap(list) : (a: A) => A[]
>wrap(list) : <A>(a: A) => A[]
>wrap : <A, B>(f: (a: A) => B) => (a: A) => B
>list : <T>(a: T) => T[]

Expand Down Expand Up @@ -180,7 +180,7 @@ const f11: <T>(x: T) => Box<T[]> = compose(list, box);
>T : T
>Box : Box<T>
>T : T
>compose(list, box) : (a: T) => Box<T[]>
>compose(list, box) : <T>(a: T) => Box<T[]>
>compose : <A, B, C>(f: (a: A) => B, g: (b: B) => C) => (a: A) => C
>list : <T>(a: T) => T[]
>box : <V>(x: V) => Box<V>
Expand Down Expand Up @@ -212,7 +212,7 @@ const f13: <T>(x: Box<T[]>) => T = compose(unbox, unlist);
>Box : Box<T>
>T : T
>T : T
>compose(unbox, unlist) : (a: Box<T[]>) => T
>compose(unbox, unlist) : <T>(a: Box<T[]>) => T
>compose : <A, B, C>(f: (a: A) => B, g: (b: B) => C) => (a: A) => C
>unbox : <W>(x: Box<W>) => W
>unlist : <T>(a: T[]) => T
Expand Down Expand Up @@ -281,7 +281,7 @@ const f22: <A>(a: A[]) => A[] = arrayMap(identity);
>a : A[]
>A : A
>A : A
>arrayMap(identity) : (a: A[]) => A[]
>arrayMap(identity) : <A>(a: A[]) => A[]
>arrayMap : <T, U>(f: (x: T) => U) => (a: T[]) => U[]
>identity : <T>(x: T) => T

Expand Down Expand Up @@ -340,7 +340,7 @@ const f40: <A, B>(b: B, a: A) => [A, B] = flip(zip);
>A : A
>A : A
>B : B
>flip(zip) : (y: B, x: A) => [A, B]
>flip(zip) : <A, B>(y: B, x: A) => [A, B]
>flip : <X, Y, Z>(f: (x: X, y: Y) => Z) => (y: Y, x: X) => Z
>zip : <A, B>(a: A, b: B) => [A, B]

Expand Down
122 changes: 122 additions & 0 deletions tests/baselines/reference/genericTypeParameterEquivalence2strict.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
//// [genericTypeParameterEquivalence2strict.ts]
// compose :: (b->c) -> (a->b) -> (a->c)
function compose<A, B, C>(f: (b: B) => C, g: (a:A) => B): (a:A) => C {
return function (a:A) : C {
return f(g.apply(null, a));
};
}

// forEach :: [a] -> (a -> ()) -> ()
function forEach<A>(list: A[], f: (a: A, n?: number) => void ): void {
for (var i = 0; i < list.length; ++i) {
f(list[i], i);
}
}

// filter :: (a->bool) -> [a] -> [a]
function filter<A>(f: (a: A) => boolean, ar: A[]): A[] {
var ret: A[] = [];
forEach(ar, (el) => {
if (f(el)) {
ret.push(el);
}
} );

return ret;
}

// length :: [a] -> Num
function length2<A>(ar: A[]): number {
return ar.length;
}

// curry1 :: ((a,b)->c) -> (a->(b->c))
function curry1<A, B, C>(f: (a: A, b: B) => C): (ax: A) => (bx: B) => C {
return function (ay: A) {
return function (by: B) {
return f(ay, by);
};
};
}

var cfilter = curry1(filter);

declare function strBool(str: string): boolean
const filterer = cfilter(strBool);
const expectFilterer: (a: string[]) => string[] = filterer;

const filtered = filterer(["hello"]);
const expectFiltered: string[] = filtered;

// compose :: (b->c) -> (a->b) -> (a->c)
// length :: [a] -> Num
// cfilter :: {} -> {} -> [{}]
// pred :: a -> Bool
// cfilter(pred) :: {} -> [{}]
// length2 :: [a] -> Num
// countWhere :: (a -> Bool) -> [a] -> Num

function countWhere_1<A>(pred: (a: A) => boolean): (a: A[]) => number {
return compose(length2, cfilter(pred));
}

function countWhere_2<A>(pred: (a: A) => boolean): (a: A[]) => number {
var where = cfilter(pred);
return compose(length2, where);
}

//// [genericTypeParameterEquivalence2strict.js]
"use strict";
// compose :: (b->c) -> (a->b) -> (a->c)
function compose(f, g) {
return function (a) {
return f(g.apply(null, a));
};
}
// forEach :: [a] -> (a -> ()) -> ()
function forEach(list, f) {
for (var i = 0; i < list.length; ++i) {
f(list[i], i);
}
}
// filter :: (a->bool) -> [a] -> [a]
function filter(f, ar) {
var ret = [];
forEach(ar, function (el) {
if (f(el)) {
ret.push(el);
}
});
return ret;
}
// length :: [a] -> Num
function length2(ar) {
return ar.length;
}
// curry1 :: ((a,b)->c) -> (a->(b->c))
function curry1(f) {
return function (ay) {
return function (by) {
return f(ay, by);
};
};
}
var cfilter = curry1(filter);
var filterer = cfilter(strBool);
var expectFilterer = filterer;
var filtered = filterer(["hello"]);
var expectFiltered = filtered;
// compose :: (b->c) -> (a->b) -> (a->c)
// length :: [a] -> Num
// cfilter :: {} -> {} -> [{}]
// pred :: a -> Bool
// cfilter(pred) :: {} -> [{}]
// length2 :: [a] -> Num
// countWhere :: (a -> Bool) -> [a] -> Num
function countWhere_1(pred) {
return compose(length2, cfilter(pred));
}
function countWhere_2(pred) {
var where = cfilter(pred);
return compose(length2, where);
}
Loading