Skip to content

Add inference priority level for conditional types in contravariant positions #35199

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
Nov 21, 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
5 changes: 4 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17552,9 +17552,12 @@ namespace ts {
inferFromTypes(getTrueTypeFromConditionalType(<ConditionalType>source), getTrueTypeFromConditionalType(<ConditionalType>target));
inferFromTypes(getFalseTypeFromConditionalType(<ConditionalType>source), getFalseTypeFromConditionalType(<ConditionalType>target));
}
else if (target.flags & TypeFlags.Conditional && !contravariant) {
else if (target.flags & TypeFlags.Conditional) {
const savePriority = priority;
priority |= contravariant ? InferencePriority.ContravariantConditional : 0;
const targetTypes = [getTrueTypeFromConditionalType(<ConditionalType>target), getFalseTypeFromConditionalType(<ConditionalType>target)];
inferToMultipleTypes(source, targetTypes, target.flags);
priority = savePriority;
}
else if (target.flags & TypeFlags.UnionOrIntersection) {
inferToMultipleTypes(source, (<UnionOrIntersectionType>target).types, target.flags);
Expand Down
11 changes: 6 additions & 5 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4740,11 +4740,12 @@ namespace ts {
HomomorphicMappedType = 1 << 1, // Reverse inference for homomorphic mapped type
PartialHomomorphicMappedType = 1 << 2, // Partial reverse inference for homomorphic mapped type
MappedTypeConstraint = 1 << 3, // Reverse inference for mapped type
ReturnType = 1 << 4, // Inference made from return type of generic function
LiteralKeyof = 1 << 5, // Inference made from a string literal to a keyof T
NoConstraints = 1 << 6, // Don't infer from constraints of instantiable types
AlwaysStrict = 1 << 7, // Always use strict rules for contravariant inferences
MaxValue = 1 << 8, // Seed for inference priority tracking
ContravariantConditional = 1 << 4, // Conditional type in contravariant position
ReturnType = 1 << 5, // Inference made from return type of generic function
LiteralKeyof = 1 << 6, // Inference made from a string literal to a keyof T
NoConstraints = 1 << 7, // Don't infer from constraints of instantiable types
AlwaysStrict = 1 << 8, // Always use strict rules for contravariant inferences
MaxValue = 1 << 9, // Seed for inference priority tracking

PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates
Circularity = -1, // Inference circularity (value less than all other priorities)
Expand Down
13 changes: 7 additions & 6 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2500,12 +2500,13 @@ declare namespace ts {
HomomorphicMappedType = 2,
PartialHomomorphicMappedType = 4,
MappedTypeConstraint = 8,
ReturnType = 16,
LiteralKeyof = 32,
NoConstraints = 64,
AlwaysStrict = 128,
MaxValue = 256,
PriorityImpliesCombination = 56,
ContravariantConditional = 16,
ReturnType = 32,
LiteralKeyof = 64,
NoConstraints = 128,
AlwaysStrict = 256,
MaxValue = 512,
PriorityImpliesCombination = 104,
Circularity = -1
}
/** @deprecated Use FileExtensionInfo instead. */
Expand Down
13 changes: 7 additions & 6 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2500,12 +2500,13 @@ declare namespace ts {
HomomorphicMappedType = 2,
PartialHomomorphicMappedType = 4,
MappedTypeConstraint = 8,
ReturnType = 16,
LiteralKeyof = 32,
NoConstraints = 64,
AlwaysStrict = 128,
MaxValue = 256,
PriorityImpliesCombination = 56,
ContravariantConditional = 16,
ReturnType = 32,
LiteralKeyof = 64,
NoConstraints = 128,
AlwaysStrict = 256,
MaxValue = 512,
PriorityImpliesCombination = 104,
Circularity = -1
}
/** @deprecated Use FileExtensionInfo instead. */
Expand Down
43 changes: 43 additions & 0 deletions tests/baselines/reference/conditionalTypes2.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -273,4 +273,47 @@ tests/cases/conformance/types/conditional/conditionalTypes2.ts(75,12): error TS2
type Hmm<T, U extends T> = U extends T ? { [K in keyof U]: number } : never;
type What = Hmm<{}, { a: string }>
const w: What = { a: 4 };

// Repro from #33568

declare function save(_response: IRootResponse<string>): void;

exportCommand(save);

declare function exportCommand<TResponse>(functionToCall: IExportCallback<TResponse>): void;

interface IExportCallback<TResponse> {
(response: IRootResponse<TResponse>): void;
}

type IRootResponse<TResponse> =
TResponse extends IRecord ? IRecordResponse<TResponse> : IResponse<TResponse>;

interface IRecord {
readonly Id: string;
}

declare type IRecordResponse<T extends IRecord> = IResponse<T> & {
sendRecord(): void;
};

declare type IResponse<T> = {
sendValue(name: keyof GetAllPropertiesOfType<T, string>): void;
};

declare type GetPropertyNamesOfType<T, RestrictToType> = {
[PropertyName in Extract<keyof T, string>]: T[PropertyName] extends RestrictToType ? PropertyName : never
}[Extract<keyof T, string>];

declare type GetAllPropertiesOfType<T, RestrictToType> = Pick<
T,
GetPropertyNamesOfType<Required<T>, RestrictToType>
>;

// Repro from #33568

declare function ff(x: Foo3<string>): void;
declare function gg<T>(f: (x: Foo3<T>) => void): void;
type Foo3<T> = T extends number ? { n: T } : { x: T };
gg(ff);

71 changes: 71 additions & 0 deletions tests/baselines/reference/conditionalTypes2.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,49 @@ type PCCB = ProductComplementComplement['b'];
type Hmm<T, U extends T> = U extends T ? { [K in keyof U]: number } : never;
type What = Hmm<{}, { a: string }>
const w: What = { a: 4 };

// Repro from #33568

declare function save(_response: IRootResponse<string>): void;

exportCommand(save);

declare function exportCommand<TResponse>(functionToCall: IExportCallback<TResponse>): void;

interface IExportCallback<TResponse> {
(response: IRootResponse<TResponse>): void;
}

type IRootResponse<TResponse> =
TResponse extends IRecord ? IRecordResponse<TResponse> : IResponse<TResponse>;

interface IRecord {
readonly Id: string;
}

declare type IRecordResponse<T extends IRecord> = IResponse<T> & {
sendRecord(): void;
};

declare type IResponse<T> = {
sendValue(name: keyof GetAllPropertiesOfType<T, string>): void;
};

declare type GetPropertyNamesOfType<T, RestrictToType> = {
[PropertyName in Extract<keyof T, string>]: T[PropertyName] extends RestrictToType ? PropertyName : never
}[Extract<keyof T, string>];

declare type GetAllPropertiesOfType<T, RestrictToType> = Pick<
T,
GetPropertyNamesOfType<Required<T>, RestrictToType>
>;

// Repro from #33568

declare function ff(x: Foo3<string>): void;
declare function gg<T>(f: (x: Foo3<T>) => void): void;
type Foo3<T> = T extends number ? { n: T } : { x: T };
gg(ff);


//// [conditionalTypes2.js]
Expand Down Expand Up @@ -272,6 +315,8 @@ function foo(value) {
}
}
var w = { a: 4 };
exportCommand(save);
gg(ff);


//// [conditionalTypes2.d.ts]
Expand Down Expand Up @@ -406,3 +451,29 @@ declare type What = Hmm<{}, {
a: string;
}>;
declare const w: What;
declare function save(_response: IRootResponse<string>): void;
declare function exportCommand<TResponse>(functionToCall: IExportCallback<TResponse>): void;
interface IExportCallback<TResponse> {
(response: IRootResponse<TResponse>): void;
}
declare type IRootResponse<TResponse> = TResponse extends IRecord ? IRecordResponse<TResponse> : IResponse<TResponse>;
interface IRecord {
readonly Id: string;
}
declare type IRecordResponse<T extends IRecord> = IResponse<T> & {
sendRecord(): void;
};
declare type IResponse<T> = {
sendValue(name: keyof GetAllPropertiesOfType<T, string>): void;
};
declare type GetPropertyNamesOfType<T, RestrictToType> = {
[PropertyName in Extract<keyof T, string>]: T[PropertyName] extends RestrictToType ? PropertyName : never;
}[Extract<keyof T, string>];
declare type GetAllPropertiesOfType<T, RestrictToType> = Pick<T, GetPropertyNamesOfType<Required<T>, RestrictToType>>;
declare function ff(x: Foo3<string>): void;
declare function gg<T>(f: (x: Foo3<T>) => void): void;
declare type Foo3<T> = T extends number ? {
n: T;
} : {
x: T;
};
134 changes: 134 additions & 0 deletions tests/baselines/reference/conditionalTypes2.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -707,3 +707,137 @@ const w: What = { a: 4 };
>What : Symbol(What, Decl(conditionalTypes2.ts, 192, 76))
>a : Symbol(a, Decl(conditionalTypes2.ts, 194, 17))

// Repro from #33568

declare function save(_response: IRootResponse<string>): void;
>save : Symbol(save, Decl(conditionalTypes2.ts, 194, 25))
>_response : Symbol(_response, Decl(conditionalTypes2.ts, 198, 22))
>IRootResponse : Symbol(IRootResponse, Decl(conditionalTypes2.ts, 206, 1))

exportCommand(save);
>exportCommand : Symbol(exportCommand, Decl(conditionalTypes2.ts, 200, 20))
>save : Symbol(save, Decl(conditionalTypes2.ts, 194, 25))

declare function exportCommand<TResponse>(functionToCall: IExportCallback<TResponse>): void;
>exportCommand : Symbol(exportCommand, Decl(conditionalTypes2.ts, 200, 20))
>TResponse : Symbol(TResponse, Decl(conditionalTypes2.ts, 202, 31))
>functionToCall : Symbol(functionToCall, Decl(conditionalTypes2.ts, 202, 42))
>IExportCallback : Symbol(IExportCallback, Decl(conditionalTypes2.ts, 202, 92))
>TResponse : Symbol(TResponse, Decl(conditionalTypes2.ts, 202, 31))

interface IExportCallback<TResponse> {
>IExportCallback : Symbol(IExportCallback, Decl(conditionalTypes2.ts, 202, 92))
>TResponse : Symbol(TResponse, Decl(conditionalTypes2.ts, 204, 26))

(response: IRootResponse<TResponse>): void;
>response : Symbol(response, Decl(conditionalTypes2.ts, 205, 2))
>IRootResponse : Symbol(IRootResponse, Decl(conditionalTypes2.ts, 206, 1))
>TResponse : Symbol(TResponse, Decl(conditionalTypes2.ts, 204, 26))
}

type IRootResponse<TResponse> =
>IRootResponse : Symbol(IRootResponse, Decl(conditionalTypes2.ts, 206, 1))
>TResponse : Symbol(TResponse, Decl(conditionalTypes2.ts, 208, 19))

TResponse extends IRecord ? IRecordResponse<TResponse> : IResponse<TResponse>;
>TResponse : Symbol(TResponse, Decl(conditionalTypes2.ts, 208, 19))
>IRecord : Symbol(IRecord, Decl(conditionalTypes2.ts, 209, 79))
>IRecordResponse : Symbol(IRecordResponse, Decl(conditionalTypes2.ts, 213, 1))
>TResponse : Symbol(TResponse, Decl(conditionalTypes2.ts, 208, 19))
>IResponse : Symbol(IResponse, Decl(conditionalTypes2.ts, 217, 2))
>TResponse : Symbol(TResponse, Decl(conditionalTypes2.ts, 208, 19))

interface IRecord {
>IRecord : Symbol(IRecord, Decl(conditionalTypes2.ts, 209, 79))

readonly Id: string;
>Id : Symbol(IRecord.Id, Decl(conditionalTypes2.ts, 211, 19))
}

declare type IRecordResponse<T extends IRecord> = IResponse<T> & {
>IRecordResponse : Symbol(IRecordResponse, Decl(conditionalTypes2.ts, 213, 1))
>T : Symbol(T, Decl(conditionalTypes2.ts, 215, 29))
>IRecord : Symbol(IRecord, Decl(conditionalTypes2.ts, 209, 79))
>IResponse : Symbol(IResponse, Decl(conditionalTypes2.ts, 217, 2))
>T : Symbol(T, Decl(conditionalTypes2.ts, 215, 29))

sendRecord(): void;
>sendRecord : Symbol(sendRecord, Decl(conditionalTypes2.ts, 215, 66))

};

declare type IResponse<T> = {
>IResponse : Symbol(IResponse, Decl(conditionalTypes2.ts, 217, 2))
>T : Symbol(T, Decl(conditionalTypes2.ts, 219, 23))

sendValue(name: keyof GetAllPropertiesOfType<T, string>): void;
>sendValue : Symbol(sendValue, Decl(conditionalTypes2.ts, 219, 29))
>name : Symbol(name, Decl(conditionalTypes2.ts, 220, 11))
>GetAllPropertiesOfType : Symbol(GetAllPropertiesOfType, Decl(conditionalTypes2.ts, 225, 28))
>T : Symbol(T, Decl(conditionalTypes2.ts, 219, 23))

};

declare type GetPropertyNamesOfType<T, RestrictToType> = {
>GetPropertyNamesOfType : Symbol(GetPropertyNamesOfType, Decl(conditionalTypes2.ts, 221, 2))
>T : Symbol(T, Decl(conditionalTypes2.ts, 223, 36))
>RestrictToType : Symbol(RestrictToType, Decl(conditionalTypes2.ts, 223, 38))

[PropertyName in Extract<keyof T, string>]: T[PropertyName] extends RestrictToType ? PropertyName : never
>PropertyName : Symbol(PropertyName, Decl(conditionalTypes2.ts, 224, 2))
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(conditionalTypes2.ts, 223, 36))
>T : Symbol(T, Decl(conditionalTypes2.ts, 223, 36))
>PropertyName : Symbol(PropertyName, Decl(conditionalTypes2.ts, 224, 2))
>RestrictToType : Symbol(RestrictToType, Decl(conditionalTypes2.ts, 223, 38))
>PropertyName : Symbol(PropertyName, Decl(conditionalTypes2.ts, 224, 2))

}[Extract<keyof T, string>];
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(conditionalTypes2.ts, 223, 36))

declare type GetAllPropertiesOfType<T, RestrictToType> = Pick<
>GetAllPropertiesOfType : Symbol(GetAllPropertiesOfType, Decl(conditionalTypes2.ts, 225, 28))
>T : Symbol(T, Decl(conditionalTypes2.ts, 227, 36))
>RestrictToType : Symbol(RestrictToType, Decl(conditionalTypes2.ts, 227, 38))
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))

T,
>T : Symbol(T, Decl(conditionalTypes2.ts, 227, 36))

GetPropertyNamesOfType<Required<T>, RestrictToType>
>GetPropertyNamesOfType : Symbol(GetPropertyNamesOfType, Decl(conditionalTypes2.ts, 221, 2))
>Required : Symbol(Required, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(conditionalTypes2.ts, 227, 36))
>RestrictToType : Symbol(RestrictToType, Decl(conditionalTypes2.ts, 227, 38))

>;

// Repro from #33568

declare function ff(x: Foo3<string>): void;
>ff : Symbol(ff, Decl(conditionalTypes2.ts, 230, 2))
>x : Symbol(x, Decl(conditionalTypes2.ts, 234, 20))
>Foo3 : Symbol(Foo3, Decl(conditionalTypes2.ts, 235, 54))

declare function gg<T>(f: (x: Foo3<T>) => void): void;
>gg : Symbol(gg, Decl(conditionalTypes2.ts, 234, 43))
>T : Symbol(T, Decl(conditionalTypes2.ts, 235, 20))
>f : Symbol(f, Decl(conditionalTypes2.ts, 235, 23))
>x : Symbol(x, Decl(conditionalTypes2.ts, 235, 27))
>Foo3 : Symbol(Foo3, Decl(conditionalTypes2.ts, 235, 54))
>T : Symbol(T, Decl(conditionalTypes2.ts, 235, 20))

type Foo3<T> = T extends number ? { n: T } : { x: T };
>Foo3 : Symbol(Foo3, Decl(conditionalTypes2.ts, 235, 54))
>T : Symbol(T, Decl(conditionalTypes2.ts, 236, 10))
>T : Symbol(T, Decl(conditionalTypes2.ts, 236, 10))
>n : Symbol(n, Decl(conditionalTypes2.ts, 236, 35))
>T : Symbol(T, Decl(conditionalTypes2.ts, 236, 10))
>x : Symbol(x, Decl(conditionalTypes2.ts, 236, 46))
>T : Symbol(T, Decl(conditionalTypes2.ts, 236, 10))

gg(ff);
>gg : Symbol(gg, Decl(conditionalTypes2.ts, 234, 43))
>ff : Symbol(ff, Decl(conditionalTypes2.ts, 230, 2))

Loading