Skip to content

Commit 94d4023

Browse files
authored
Add inference priority level for conditional types in contravariant positions (#35199)
* Add inference priority level for conditional types in contravariant positions * Accept new API baselines * Add regression tests * Accept new baselines
1 parent 84c84d8 commit 94d4023

File tree

9 files changed

+396
-18
lines changed

9 files changed

+396
-18
lines changed

src/compiler/checker.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -17552,9 +17552,12 @@ namespace ts {
1755217552
inferFromTypes(getTrueTypeFromConditionalType(<ConditionalType>source), getTrueTypeFromConditionalType(<ConditionalType>target));
1755317553
inferFromTypes(getFalseTypeFromConditionalType(<ConditionalType>source), getFalseTypeFromConditionalType(<ConditionalType>target));
1755417554
}
17555-
else if (target.flags & TypeFlags.Conditional && !contravariant) {
17555+
else if (target.flags & TypeFlags.Conditional) {
17556+
const savePriority = priority;
17557+
priority |= contravariant ? InferencePriority.ContravariantConditional : 0;
1755617558
const targetTypes = [getTrueTypeFromConditionalType(<ConditionalType>target), getFalseTypeFromConditionalType(<ConditionalType>target)];
1755717559
inferToMultipleTypes(source, targetTypes, target.flags);
17560+
priority = savePriority;
1755817561
}
1755917562
else if (target.flags & TypeFlags.UnionOrIntersection) {
1756017563
inferToMultipleTypes(source, (<UnionOrIntersectionType>target).types, target.flags);

src/compiler/types.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -4740,11 +4740,12 @@ namespace ts {
47404740
HomomorphicMappedType = 1 << 1, // Reverse inference for homomorphic mapped type
47414741
PartialHomomorphicMappedType = 1 << 2, // Partial reverse inference for homomorphic mapped type
47424742
MappedTypeConstraint = 1 << 3, // Reverse inference for mapped type
4743-
ReturnType = 1 << 4, // Inference made from return type of generic function
4744-
LiteralKeyof = 1 << 5, // Inference made from a string literal to a keyof T
4745-
NoConstraints = 1 << 6, // Don't infer from constraints of instantiable types
4746-
AlwaysStrict = 1 << 7, // Always use strict rules for contravariant inferences
4747-
MaxValue = 1 << 8, // Seed for inference priority tracking
4743+
ContravariantConditional = 1 << 4, // Conditional type in contravariant position
4744+
ReturnType = 1 << 5, // Inference made from return type of generic function
4745+
LiteralKeyof = 1 << 6, // Inference made from a string literal to a keyof T
4746+
NoConstraints = 1 << 7, // Don't infer from constraints of instantiable types
4747+
AlwaysStrict = 1 << 8, // Always use strict rules for contravariant inferences
4748+
MaxValue = 1 << 9, // Seed for inference priority tracking
47484749

47494750
PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates
47504751
Circularity = -1, // Inference circularity (value less than all other priorities)

tests/baselines/reference/api/tsserverlibrary.d.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -2500,12 +2500,13 @@ declare namespace ts {
25002500
HomomorphicMappedType = 2,
25012501
PartialHomomorphicMappedType = 4,
25022502
MappedTypeConstraint = 8,
2503-
ReturnType = 16,
2504-
LiteralKeyof = 32,
2505-
NoConstraints = 64,
2506-
AlwaysStrict = 128,
2507-
MaxValue = 256,
2508-
PriorityImpliesCombination = 56,
2503+
ContravariantConditional = 16,
2504+
ReturnType = 32,
2505+
LiteralKeyof = 64,
2506+
NoConstraints = 128,
2507+
AlwaysStrict = 256,
2508+
MaxValue = 512,
2509+
PriorityImpliesCombination = 104,
25092510
Circularity = -1
25102511
}
25112512
/** @deprecated Use FileExtensionInfo instead. */

tests/baselines/reference/api/typescript.d.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -2500,12 +2500,13 @@ declare namespace ts {
25002500
HomomorphicMappedType = 2,
25012501
PartialHomomorphicMappedType = 4,
25022502
MappedTypeConstraint = 8,
2503-
ReturnType = 16,
2504-
LiteralKeyof = 32,
2505-
NoConstraints = 64,
2506-
AlwaysStrict = 128,
2507-
MaxValue = 256,
2508-
PriorityImpliesCombination = 56,
2503+
ContravariantConditional = 16,
2504+
ReturnType = 32,
2505+
LiteralKeyof = 64,
2506+
NoConstraints = 128,
2507+
AlwaysStrict = 256,
2508+
MaxValue = 512,
2509+
PriorityImpliesCombination = 104,
25092510
Circularity = -1
25102511
}
25112512
/** @deprecated Use FileExtensionInfo instead. */

tests/baselines/reference/conditionalTypes2.errors.txt

+43
Original file line numberDiff line numberDiff line change
@@ -273,4 +273,47 @@ tests/cases/conformance/types/conditional/conditionalTypes2.ts(75,12): error TS2
273273
type Hmm<T, U extends T> = U extends T ? { [K in keyof U]: number } : never;
274274
type What = Hmm<{}, { a: string }>
275275
const w: What = { a: 4 };
276+
277+
// Repro from #33568
278+
279+
declare function save(_response: IRootResponse<string>): void;
280+
281+
exportCommand(save);
282+
283+
declare function exportCommand<TResponse>(functionToCall: IExportCallback<TResponse>): void;
284+
285+
interface IExportCallback<TResponse> {
286+
(response: IRootResponse<TResponse>): void;
287+
}
288+
289+
type IRootResponse<TResponse> =
290+
TResponse extends IRecord ? IRecordResponse<TResponse> : IResponse<TResponse>;
291+
292+
interface IRecord {
293+
readonly Id: string;
294+
}
295+
296+
declare type IRecordResponse<T extends IRecord> = IResponse<T> & {
297+
sendRecord(): void;
298+
};
299+
300+
declare type IResponse<T> = {
301+
sendValue(name: keyof GetAllPropertiesOfType<T, string>): void;
302+
};
303+
304+
declare type GetPropertyNamesOfType<T, RestrictToType> = {
305+
[PropertyName in Extract<keyof T, string>]: T[PropertyName] extends RestrictToType ? PropertyName : never
306+
}[Extract<keyof T, string>];
307+
308+
declare type GetAllPropertiesOfType<T, RestrictToType> = Pick<
309+
T,
310+
GetPropertyNamesOfType<Required<T>, RestrictToType>
311+
>;
312+
313+
// Repro from #33568
314+
315+
declare function ff(x: Foo3<string>): void;
316+
declare function gg<T>(f: (x: Foo3<T>) => void): void;
317+
type Foo3<T> = T extends number ? { n: T } : { x: T };
318+
gg(ff);
276319

tests/baselines/reference/conditionalTypes2.js

+71
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,49 @@ type PCCB = ProductComplementComplement['b'];
194194
type Hmm<T, U extends T> = U extends T ? { [K in keyof U]: number } : never;
195195
type What = Hmm<{}, { a: string }>
196196
const w: What = { a: 4 };
197+
198+
// Repro from #33568
199+
200+
declare function save(_response: IRootResponse<string>): void;
201+
202+
exportCommand(save);
203+
204+
declare function exportCommand<TResponse>(functionToCall: IExportCallback<TResponse>): void;
205+
206+
interface IExportCallback<TResponse> {
207+
(response: IRootResponse<TResponse>): void;
208+
}
209+
210+
type IRootResponse<TResponse> =
211+
TResponse extends IRecord ? IRecordResponse<TResponse> : IResponse<TResponse>;
212+
213+
interface IRecord {
214+
readonly Id: string;
215+
}
216+
217+
declare type IRecordResponse<T extends IRecord> = IResponse<T> & {
218+
sendRecord(): void;
219+
};
220+
221+
declare type IResponse<T> = {
222+
sendValue(name: keyof GetAllPropertiesOfType<T, string>): void;
223+
};
224+
225+
declare type GetPropertyNamesOfType<T, RestrictToType> = {
226+
[PropertyName in Extract<keyof T, string>]: T[PropertyName] extends RestrictToType ? PropertyName : never
227+
}[Extract<keyof T, string>];
228+
229+
declare type GetAllPropertiesOfType<T, RestrictToType> = Pick<
230+
T,
231+
GetPropertyNamesOfType<Required<T>, RestrictToType>
232+
>;
233+
234+
// Repro from #33568
235+
236+
declare function ff(x: Foo3<string>): void;
237+
declare function gg<T>(f: (x: Foo3<T>) => void): void;
238+
type Foo3<T> = T extends number ? { n: T } : { x: T };
239+
gg(ff);
197240

198241

199242
//// [conditionalTypes2.js]
@@ -272,6 +315,8 @@ function foo(value) {
272315
}
273316
}
274317
var w = { a: 4 };
318+
exportCommand(save);
319+
gg(ff);
275320

276321

277322
//// [conditionalTypes2.d.ts]
@@ -406,3 +451,29 @@ declare type What = Hmm<{}, {
406451
a: string;
407452
}>;
408453
declare const w: What;
454+
declare function save(_response: IRootResponse<string>): void;
455+
declare function exportCommand<TResponse>(functionToCall: IExportCallback<TResponse>): void;
456+
interface IExportCallback<TResponse> {
457+
(response: IRootResponse<TResponse>): void;
458+
}
459+
declare type IRootResponse<TResponse> = TResponse extends IRecord ? IRecordResponse<TResponse> : IResponse<TResponse>;
460+
interface IRecord {
461+
readonly Id: string;
462+
}
463+
declare type IRecordResponse<T extends IRecord> = IResponse<T> & {
464+
sendRecord(): void;
465+
};
466+
declare type IResponse<T> = {
467+
sendValue(name: keyof GetAllPropertiesOfType<T, string>): void;
468+
};
469+
declare type GetPropertyNamesOfType<T, RestrictToType> = {
470+
[PropertyName in Extract<keyof T, string>]: T[PropertyName] extends RestrictToType ? PropertyName : never;
471+
}[Extract<keyof T, string>];
472+
declare type GetAllPropertiesOfType<T, RestrictToType> = Pick<T, GetPropertyNamesOfType<Required<T>, RestrictToType>>;
473+
declare function ff(x: Foo3<string>): void;
474+
declare function gg<T>(f: (x: Foo3<T>) => void): void;
475+
declare type Foo3<T> = T extends number ? {
476+
n: T;
477+
} : {
478+
x: T;
479+
};

tests/baselines/reference/conditionalTypes2.symbols

+134
Original file line numberDiff line numberDiff line change
@@ -707,3 +707,137 @@ const w: What = { a: 4 };
707707
>What : Symbol(What, Decl(conditionalTypes2.ts, 192, 76))
708708
>a : Symbol(a, Decl(conditionalTypes2.ts, 194, 17))
709709

710+
// Repro from #33568
711+
712+
declare function save(_response: IRootResponse<string>): void;
713+
>save : Symbol(save, Decl(conditionalTypes2.ts, 194, 25))
714+
>_response : Symbol(_response, Decl(conditionalTypes2.ts, 198, 22))
715+
>IRootResponse : Symbol(IRootResponse, Decl(conditionalTypes2.ts, 206, 1))
716+
717+
exportCommand(save);
718+
>exportCommand : Symbol(exportCommand, Decl(conditionalTypes2.ts, 200, 20))
719+
>save : Symbol(save, Decl(conditionalTypes2.ts, 194, 25))
720+
721+
declare function exportCommand<TResponse>(functionToCall: IExportCallback<TResponse>): void;
722+
>exportCommand : Symbol(exportCommand, Decl(conditionalTypes2.ts, 200, 20))
723+
>TResponse : Symbol(TResponse, Decl(conditionalTypes2.ts, 202, 31))
724+
>functionToCall : Symbol(functionToCall, Decl(conditionalTypes2.ts, 202, 42))
725+
>IExportCallback : Symbol(IExportCallback, Decl(conditionalTypes2.ts, 202, 92))
726+
>TResponse : Symbol(TResponse, Decl(conditionalTypes2.ts, 202, 31))
727+
728+
interface IExportCallback<TResponse> {
729+
>IExportCallback : Symbol(IExportCallback, Decl(conditionalTypes2.ts, 202, 92))
730+
>TResponse : Symbol(TResponse, Decl(conditionalTypes2.ts, 204, 26))
731+
732+
(response: IRootResponse<TResponse>): void;
733+
>response : Symbol(response, Decl(conditionalTypes2.ts, 205, 2))
734+
>IRootResponse : Symbol(IRootResponse, Decl(conditionalTypes2.ts, 206, 1))
735+
>TResponse : Symbol(TResponse, Decl(conditionalTypes2.ts, 204, 26))
736+
}
737+
738+
type IRootResponse<TResponse> =
739+
>IRootResponse : Symbol(IRootResponse, Decl(conditionalTypes2.ts, 206, 1))
740+
>TResponse : Symbol(TResponse, Decl(conditionalTypes2.ts, 208, 19))
741+
742+
TResponse extends IRecord ? IRecordResponse<TResponse> : IResponse<TResponse>;
743+
>TResponse : Symbol(TResponse, Decl(conditionalTypes2.ts, 208, 19))
744+
>IRecord : Symbol(IRecord, Decl(conditionalTypes2.ts, 209, 79))
745+
>IRecordResponse : Symbol(IRecordResponse, Decl(conditionalTypes2.ts, 213, 1))
746+
>TResponse : Symbol(TResponse, Decl(conditionalTypes2.ts, 208, 19))
747+
>IResponse : Symbol(IResponse, Decl(conditionalTypes2.ts, 217, 2))
748+
>TResponse : Symbol(TResponse, Decl(conditionalTypes2.ts, 208, 19))
749+
750+
interface IRecord {
751+
>IRecord : Symbol(IRecord, Decl(conditionalTypes2.ts, 209, 79))
752+
753+
readonly Id: string;
754+
>Id : Symbol(IRecord.Id, Decl(conditionalTypes2.ts, 211, 19))
755+
}
756+
757+
declare type IRecordResponse<T extends IRecord> = IResponse<T> & {
758+
>IRecordResponse : Symbol(IRecordResponse, Decl(conditionalTypes2.ts, 213, 1))
759+
>T : Symbol(T, Decl(conditionalTypes2.ts, 215, 29))
760+
>IRecord : Symbol(IRecord, Decl(conditionalTypes2.ts, 209, 79))
761+
>IResponse : Symbol(IResponse, Decl(conditionalTypes2.ts, 217, 2))
762+
>T : Symbol(T, Decl(conditionalTypes2.ts, 215, 29))
763+
764+
sendRecord(): void;
765+
>sendRecord : Symbol(sendRecord, Decl(conditionalTypes2.ts, 215, 66))
766+
767+
};
768+
769+
declare type IResponse<T> = {
770+
>IResponse : Symbol(IResponse, Decl(conditionalTypes2.ts, 217, 2))
771+
>T : Symbol(T, Decl(conditionalTypes2.ts, 219, 23))
772+
773+
sendValue(name: keyof GetAllPropertiesOfType<T, string>): void;
774+
>sendValue : Symbol(sendValue, Decl(conditionalTypes2.ts, 219, 29))
775+
>name : Symbol(name, Decl(conditionalTypes2.ts, 220, 11))
776+
>GetAllPropertiesOfType : Symbol(GetAllPropertiesOfType, Decl(conditionalTypes2.ts, 225, 28))
777+
>T : Symbol(T, Decl(conditionalTypes2.ts, 219, 23))
778+
779+
};
780+
781+
declare type GetPropertyNamesOfType<T, RestrictToType> = {
782+
>GetPropertyNamesOfType : Symbol(GetPropertyNamesOfType, Decl(conditionalTypes2.ts, 221, 2))
783+
>T : Symbol(T, Decl(conditionalTypes2.ts, 223, 36))
784+
>RestrictToType : Symbol(RestrictToType, Decl(conditionalTypes2.ts, 223, 38))
785+
786+
[PropertyName in Extract<keyof T, string>]: T[PropertyName] extends RestrictToType ? PropertyName : never
787+
>PropertyName : Symbol(PropertyName, Decl(conditionalTypes2.ts, 224, 2))
788+
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
789+
>T : Symbol(T, Decl(conditionalTypes2.ts, 223, 36))
790+
>T : Symbol(T, Decl(conditionalTypes2.ts, 223, 36))
791+
>PropertyName : Symbol(PropertyName, Decl(conditionalTypes2.ts, 224, 2))
792+
>RestrictToType : Symbol(RestrictToType, Decl(conditionalTypes2.ts, 223, 38))
793+
>PropertyName : Symbol(PropertyName, Decl(conditionalTypes2.ts, 224, 2))
794+
795+
}[Extract<keyof T, string>];
796+
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
797+
>T : Symbol(T, Decl(conditionalTypes2.ts, 223, 36))
798+
799+
declare type GetAllPropertiesOfType<T, RestrictToType> = Pick<
800+
>GetAllPropertiesOfType : Symbol(GetAllPropertiesOfType, Decl(conditionalTypes2.ts, 225, 28))
801+
>T : Symbol(T, Decl(conditionalTypes2.ts, 227, 36))
802+
>RestrictToType : Symbol(RestrictToType, Decl(conditionalTypes2.ts, 227, 38))
803+
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
804+
805+
T,
806+
>T : Symbol(T, Decl(conditionalTypes2.ts, 227, 36))
807+
808+
GetPropertyNamesOfType<Required<T>, RestrictToType>
809+
>GetPropertyNamesOfType : Symbol(GetPropertyNamesOfType, Decl(conditionalTypes2.ts, 221, 2))
810+
>Required : Symbol(Required, Decl(lib.es5.d.ts, --, --))
811+
>T : Symbol(T, Decl(conditionalTypes2.ts, 227, 36))
812+
>RestrictToType : Symbol(RestrictToType, Decl(conditionalTypes2.ts, 227, 38))
813+
814+
>;
815+
816+
// Repro from #33568
817+
818+
declare function ff(x: Foo3<string>): void;
819+
>ff : Symbol(ff, Decl(conditionalTypes2.ts, 230, 2))
820+
>x : Symbol(x, Decl(conditionalTypes2.ts, 234, 20))
821+
>Foo3 : Symbol(Foo3, Decl(conditionalTypes2.ts, 235, 54))
822+
823+
declare function gg<T>(f: (x: Foo3<T>) => void): void;
824+
>gg : Symbol(gg, Decl(conditionalTypes2.ts, 234, 43))
825+
>T : Symbol(T, Decl(conditionalTypes2.ts, 235, 20))
826+
>f : Symbol(f, Decl(conditionalTypes2.ts, 235, 23))
827+
>x : Symbol(x, Decl(conditionalTypes2.ts, 235, 27))
828+
>Foo3 : Symbol(Foo3, Decl(conditionalTypes2.ts, 235, 54))
829+
>T : Symbol(T, Decl(conditionalTypes2.ts, 235, 20))
830+
831+
type Foo3<T> = T extends number ? { n: T } : { x: T };
832+
>Foo3 : Symbol(Foo3, Decl(conditionalTypes2.ts, 235, 54))
833+
>T : Symbol(T, Decl(conditionalTypes2.ts, 236, 10))
834+
>T : Symbol(T, Decl(conditionalTypes2.ts, 236, 10))
835+
>n : Symbol(n, Decl(conditionalTypes2.ts, 236, 35))
836+
>T : Symbol(T, Decl(conditionalTypes2.ts, 236, 10))
837+
>x : Symbol(x, Decl(conditionalTypes2.ts, 236, 46))
838+
>T : Symbol(T, Decl(conditionalTypes2.ts, 236, 10))
839+
840+
gg(ff);
841+
>gg : Symbol(gg, Decl(conditionalTypes2.ts, 234, 43))
842+
>ff : Symbol(ff, Decl(conditionalTypes2.ts, 230, 2))
843+

0 commit comments

Comments
 (0)