Skip to content

Commit 4827335

Browse files
committed
Normalize conditional types once when relating them and add more tests
1 parent 95404f2 commit 4827335

7 files changed

Lines changed: 862 additions & 28 deletions

File tree

‎tsc/internal/checker/checker.go‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23401,14 +23401,16 @@ func (c *Checker) getDistributedTypeFromTypeParameter(t *Type) *Type {
2340123401
return tp.distributedType
2340223402
}
2340323403

23404-
// Returns an identity mapper for the non-distributed form of the given type if it is a distributed type
23405-
// parameter. Instantiating with this mapper maps all occurrences of the distributed type parameter back
23406-
// to the non-distributed form.
23407-
func getNonDistributingMapper(t *Type) *TypeMapper {
23408-
if typeParameter := getNonDistributedTypeParameter(t); typeParameter != t {
23409-
return newSimpleTypeMapper(typeParameter, typeParameter)
23404+
// Returns the given conditional type with references to the distributed form of its check type parameter
23405+
// mapped back to the non-distributed form. This is the identity instantiation of the conditional type,
23406+
// which is cached in the instantiation map of the conditional type's root.
23407+
func (c *Checker) getNonDistributedConditionalType(t *Type) *Type {
23408+
if typeParameter := getNonDistributedTypeParameter(t.AsConditionalType().checkType); typeParameter != t.AsConditionalType().checkType {
23409+
if instantiated := c.instantiateType(t, newSimpleTypeMapper(typeParameter, typeParameter)); instantiated.flags&TypeFlagsConditional != 0 {
23410+
return instantiated
23411+
}
2341023412
}
23411-
return nil
23413+
return t
2341223414
}
2341323415

2341423416
func getNonDistributedTypeParameter(t *Type) *Type {

‎tsc/internal/checker/relater.go‎

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3757,33 +3757,28 @@ func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, repo
37573757
return TernaryMaybe
37583758
}
37593759
if target.flags&TypeFlagsConditional != 0 {
3760+
// The constituent types of an uninstantiated distributive conditional type reference the distributed
3761+
// form of the check type parameter. Normalize both sides to their non-distributed form such that the
3762+
// comparison doesn't depend on whether either side has been instantiated.
3763+
s := r.c.getNonDistributedConditionalType(source)
3764+
t := r.c.getNonDistributedConditionalType(target)
37603765
// Two conditional types 'T1 extends U1 ? X1 : Y1' and 'T2 extends U2 ? X2 : Y2' are related if
37613766
// one of T1 and T2 is related to the other, U1 and U2 are identical types, X1 is related to X2,
37623767
// and Y1 is related to Y2.
3763-
sourceParams := source.AsConditionalType().root.inferTypeParameters
3764-
// The constituent types of a distributive conditional type reference the distributed form of the
3765-
// check type parameter. Map those references back to the non-distributed form on both sides so
3766-
// that the comparison doesn't depend on whether either side has been instantiated.
3767-
sourceMapper := getNonDistributingMapper(source.AsConditionalType().checkType)
3768-
targetMapper := getNonDistributingMapper(target.AsConditionalType().checkType)
3769-
// The infer type parameters of the source are in scope in its extends type and true type only
3770-
sourceTrueMapper := sourceMapper
3768+
sourceParams := s.AsConditionalType().root.inferTypeParameters
3769+
sourceExtends := s.AsConditionalType().extendsType
3770+
var mapper *TypeMapper
37713771
if len(sourceParams) != 0 {
37723772
// If the source has infer type parameters, we instantiate them in the context of the target
37733773
ctx := r.c.newInferenceContext(sourceParams, nil /*signature*/, InferenceFlagsNone, r.isRelatedToWorker)
3774-
r.c.inferTypes(ctx.inferences, target.AsConditionalType().extendsType, source.AsConditionalType().extendsType, InferencePriorityNoConstraints|InferencePriorityAlwaysStrict, false)
3775-
sourceTrueMapper = r.c.combineTypeMappers(sourceMapper, ctx.mapper)
3776-
}
3777-
sourceExtends := r.c.instantiateType(source.AsConditionalType().extendsType, sourceTrueMapper)
3778-
targetExtends := r.c.instantiateType(target.AsConditionalType().extendsType, targetMapper)
3779-
if r.c.isTypeIdenticalTo(sourceExtends, targetExtends) && (r.isRelatedTo(source.AsConditionalType().checkType, target.AsConditionalType().checkType, RecursionFlagsBoth, false) != 0 || r.isRelatedTo(target.AsConditionalType().checkType, source.AsConditionalType().checkType, RecursionFlagsBoth, false) != 0) {
3780-
sourceTrueType := r.c.instantiateType(r.c.getTrueTypeFromConditionalType(source), sourceTrueMapper)
3781-
targetTrueType := r.c.instantiateType(r.c.getTrueTypeFromConditionalType(target), targetMapper)
3782-
result = r.isRelatedTo(sourceTrueType, targetTrueType, RecursionFlagsBoth, reportErrors)
3774+
r.c.inferTypes(ctx.inferences, t.AsConditionalType().extendsType, sourceExtends, InferencePriorityNoConstraints|InferencePriorityAlwaysStrict, false)
3775+
sourceExtends = r.c.instantiateType(sourceExtends, ctx.mapper)
3776+
mapper = ctx.mapper
3777+
}
3778+
if r.c.isTypeIdenticalTo(sourceExtends, t.AsConditionalType().extendsType) && (r.isRelatedTo(s.AsConditionalType().checkType, t.AsConditionalType().checkType, RecursionFlagsBoth, false) != 0 || r.isRelatedTo(t.AsConditionalType().checkType, s.AsConditionalType().checkType, RecursionFlagsBoth, false) != 0) {
3779+
result = r.isRelatedTo(r.c.instantiateType(r.c.getTrueTypeFromConditionalType(s), mapper), r.c.getTrueTypeFromConditionalType(t), RecursionFlagsBoth, reportErrors)
37833780
if result != TernaryFalse {
3784-
sourceFalseType := r.c.instantiateType(r.c.getFalseTypeFromConditionalType(source), sourceMapper)
3785-
targetFalseType := r.c.instantiateType(r.c.getFalseTypeFromConditionalType(target), targetMapper)
3786-
result &= r.isRelatedTo(sourceFalseType, targetFalseType, RecursionFlagsBoth, reportErrors)
3781+
result &= r.isRelatedTo(r.c.getFalseTypeFromConditionalType(s), r.c.getFalseTypeFromConditionalType(t), RecursionFlagsBoth, reportErrors)
37873782
}
37883783
if result != TernaryFalse {
37893784
return result

‎tsc/testdata/baselines/reference/conformance/conditionalTypes1.errors.txt‎

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,18 @@ conditionalTypes1.ts(288,43): error TS2322: Type 'T95<U>' is not assignable to t
7777
Type 'number | boolean' is not assignable to type 'T94<U>'.
7878
Type 'number' is not assignable to type 'T94<U>'.
7979
Type 'boolean' is not assignable to type 'true'.
80+
conditionalTypes1.ts(447,5): error TS2322: Type 'T' is not assignable to type 'T extends string ? T : never'.
81+
conditionalTypes1.ts(448,5): error TS2322: Type 'T extends string ? U : never' is not assignable to type 'T extends string ? T : never'.
82+
Type 'U' is not assignable to type 'T extends string ? T : never'.
83+
Type 'U' is not assignable to type 'T'.
84+
'T' could be instantiated with an arbitrary type which could be unrelated to 'U'.
85+
conditionalTypes1.ts(449,5): error TS2322: Type 'T extends string ? T : never' is not assignable to type 'T extends string ? U : never'.
86+
Type 'string & T' is not assignable to type 'T extends string ? U : never'.
87+
Type 'string & T' is not assignable to type 'U'.
88+
'U' could be instantiated with an arbitrary type which could be unrelated to 'string & T'.
8089

8190

82-
==== conditionalTypes1.ts (20 errors) ====
91+
==== conditionalTypes1.ts (23 errors) ====
8392
type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
8493
type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c"
8594

@@ -590,4 +599,82 @@ conditionalTypes1.ts(288,43): error TS2322: Type 'T95<U>' is not assignable to t
590599
const make = <V>(v: V): T extends { self: T } ? { t: T, v: V } : never => null!;
591600
x = make(1);
592601
}
602+
603+
function conditionalRelations7<T>(
604+
x: T extends object ? { inner: [T] extends [string] ? { t: T } : never } : never,
605+
y: [T] extends [string] ? { t: T } : never,
606+
) {
607+
x.inner = y;
608+
y = x.inner;
609+
}
610+
611+
declare function pickT<V>(v: { t: V }): V;
612+
function conditionalRelations8<T>(x: T extends object ? { t: T } : never, t: T) {
613+
let r = pickT(x);
614+
r = t;
615+
}
616+
617+
interface Box<X> { value: X }
618+
function conditionalRelations9<T>(
619+
x: Box<T extends { value: infer U } ? { t: T, u: U } : never>,
620+
y: Box<T extends { value: infer U } ? { t: T, u: U } : never>,
621+
) {
622+
x = y;
623+
y = x;
624+
}
625+
626+
type Cond<T> = T extends { value: infer U } ? { t: T, u: U } : never;
627+
type Rec<T> = T extends object ? { next: Rec<T>, t: T } : never;
628+
function conditionalRelations10<T>(x: Cond<T>, y: T extends { value: infer U } ? { t: T, u: U } : never) {
629+
x = y;
630+
y = x;
631+
const make = <V>(v: V): Cond<T> => null!;
632+
x = make(1);
633+
}
634+
function conditionalRelations11<T>(x: Rec<T>, y: T extends object ? { next: Rec<T>, t: T } : never) {
635+
x = y;
636+
y = x;
637+
}
638+
639+
function conditionalRelations12<T, U>(x: T extends string ? T : never, y: T, z: T extends string ? U : never) {
640+
x = y; // Error
641+
~
642+
!!! error TS2322: Type 'T' is not assignable to type 'T extends string ? T : never'.
643+
x = z; // Error
644+
~
645+
!!! error TS2322: Type 'T extends string ? U : never' is not assignable to type 'T extends string ? T : never'.
646+
!!! error TS2322: Type 'U' is not assignable to type 'T extends string ? T : never'.
647+
!!! error TS2322: Type 'U' is not assignable to type 'T'.
648+
!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'U'.
649+
!!! related TS2208 conditionalTypes1.ts:446:36: This type parameter might need an `extends T` constraint.
650+
!!! related TS2208 conditionalTypes1.ts:446:36: This type parameter might need an `extends T extends string ? T : never` constraint.
651+
z = x; // Error
652+
~
653+
!!! error TS2322: Type 'T extends string ? T : never' is not assignable to type 'T extends string ? U : never'.
654+
!!! error TS2322: Type 'string & T' is not assignable to type 'T extends string ? U : never'.
655+
!!! error TS2322: Type 'string & T' is not assignable to type 'U'.
656+
!!! error TS2322: 'U' could be instantiated with an arbitrary type which could be unrelated to 'string & T'.
657+
}
658+
659+
type ElementType =
660+
| { type: "text"; text: string }
661+
| { type: "image"; src: string };
662+
interface Page {
663+
addElement<T extends ElementType["type"] | (string & {})>(
664+
attrs: { type: T } & (T extends ElementType["type"]
665+
? Partial<Omit<Extract<ElementType, { type: T }>, "type">>
666+
: Record<string, any>),
667+
): void;
668+
}
669+
declare const returned: {
670+
addElement<T extends ElementType["type"] | (string & {})>(
671+
attrs: { type: T } & (T extends ElementType["type"]
672+
? Partial<Omit<Extract<ElementType, { type: T }>, "type">>
673+
: Record<string, any>),
674+
): void;
675+
};
676+
const page: Page = returned;
677+
678+
declare let f: <A>(m: A, r: (a: A extends string ? A : 0) => void) => void;
679+
let g: <A>(m: A, r: (a: A extends string ? A : 0) => void) => void = f;
593680

‎tsc/testdata/baselines/reference/conformance/conditionalTypes1.js‎

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,70 @@ function conditionalRelations6<T>(x: T extends { self: T } ? { t: T } : never) {
409409
const make = <V>(v: V): T extends { self: T } ? { t: T, v: V } : never => null!;
410410
x = make(1);
411411
}
412+
413+
function conditionalRelations7<T>(
414+
x: T extends object ? { inner: [T] extends [string] ? { t: T } : never } : never,
415+
y: [T] extends [string] ? { t: T } : never,
416+
) {
417+
x.inner = y;
418+
y = x.inner;
419+
}
420+
421+
declare function pickT<V>(v: { t: V }): V;
422+
function conditionalRelations8<T>(x: T extends object ? { t: T } : never, t: T) {
423+
let r = pickT(x);
424+
r = t;
425+
}
426+
427+
interface Box<X> { value: X }
428+
function conditionalRelations9<T>(
429+
x: Box<T extends { value: infer U } ? { t: T, u: U } : never>,
430+
y: Box<T extends { value: infer U } ? { t: T, u: U } : never>,
431+
) {
432+
x = y;
433+
y = x;
434+
}
435+
436+
type Cond<T> = T extends { value: infer U } ? { t: T, u: U } : never;
437+
type Rec<T> = T extends object ? { next: Rec<T>, t: T } : never;
438+
function conditionalRelations10<T>(x: Cond<T>, y: T extends { value: infer U } ? { t: T, u: U } : never) {
439+
x = y;
440+
y = x;
441+
const make = <V>(v: V): Cond<T> => null!;
442+
x = make(1);
443+
}
444+
function conditionalRelations11<T>(x: Rec<T>, y: T extends object ? { next: Rec<T>, t: T } : never) {
445+
x = y;
446+
y = x;
447+
}
448+
449+
function conditionalRelations12<T, U>(x: T extends string ? T : never, y: T, z: T extends string ? U : never) {
450+
x = y; // Error
451+
x = z; // Error
452+
z = x; // Error
453+
}
454+
455+
type ElementType =
456+
| { type: "text"; text: string }
457+
| { type: "image"; src: string };
458+
interface Page {
459+
addElement<T extends ElementType["type"] | (string & {})>(
460+
attrs: { type: T } & (T extends ElementType["type"]
461+
? Partial<Omit<Extract<ElementType, { type: T }>, "type">>
462+
: Record<string, any>),
463+
): void;
464+
}
465+
declare const returned: {
466+
addElement<T extends ElementType["type"] | (string & {})>(
467+
attrs: { type: T } & (T extends ElementType["type"]
468+
? Partial<Omit<Extract<ElementType, { type: T }>, "type">>
469+
: Record<string, any>),
470+
): void;
471+
};
472+
const page: Page = returned;
473+
474+
declare let f: <A>(m: A, r: (a: A extends string ? A : 0) => void) => void;
475+
let g: <A>(m: A, r: (a: A extends string ? A : 0) => void) => void = f;
412476

413477

414478
//// [conditionalTypes1.js]
@@ -537,6 +601,35 @@ function conditionalRelations6(x) {
537601
const make = (v) => null;
538602
x = make(1);
539603
}
604+
function conditionalRelations7(x, y) {
605+
x.inner = y;
606+
y = x.inner;
607+
}
608+
function conditionalRelations8(x, t) {
609+
let r = pickT(x);
610+
r = t;
611+
}
612+
function conditionalRelations9(x, y) {
613+
x = y;
614+
y = x;
615+
}
616+
function conditionalRelations10(x, y) {
617+
x = y;
618+
y = x;
619+
const make = (v) => null;
620+
x = make(1);
621+
}
622+
function conditionalRelations11(x, y) {
623+
x = y;
624+
y = x;
625+
}
626+
function conditionalRelations12(x, y, z) {
627+
x = y; // Error
628+
x = z; // Error
629+
z = x; // Error
630+
}
631+
const page = returned;
632+
let g = f;
540633

541634

542635
//// [conditionalTypes1.d.ts]
@@ -851,3 +944,75 @@ declare function conditionalRelations6<T>(x: T extends {
851944
} ? {
852945
t: T;
853946
} : never): void;
947+
declare function conditionalRelations7<T>(x: T extends object ? {
948+
inner: [T] extends [string] ? {
949+
t: T;
950+
} : never;
951+
} : never, y: [T] extends [string] ? {
952+
t: T;
953+
} : never): void;
954+
declare function pickT<V>(v: {
955+
t: V;
956+
}): V;
957+
declare function conditionalRelations8<T>(x: T extends object ? {
958+
t: T;
959+
} : never, t: T): void;
960+
interface Box<X> {
961+
value: X;
962+
}
963+
declare function conditionalRelations9<T>(x: Box<T extends {
964+
value: infer U;
965+
} ? {
966+
t: T;
967+
u: U;
968+
} : never>, y: Box<T extends {
969+
value: infer U;
970+
} ? {
971+
t: T;
972+
u: U;
973+
} : never>): void;
974+
type Cond<T> = T extends {
975+
value: infer U;
976+
} ? {
977+
t: T;
978+
u: U;
979+
} : never;
980+
type Rec<T> = T extends object ? {
981+
next: Rec<T>;
982+
t: T;
983+
} : never;
984+
declare function conditionalRelations10<T>(x: Cond<T>, y: T extends {
985+
value: infer U;
986+
} ? {
987+
t: T;
988+
u: U;
989+
} : never): void;
990+
declare function conditionalRelations11<T>(x: Rec<T>, y: T extends object ? {
991+
next: Rec<T>;
992+
t: T;
993+
} : never): void;
994+
declare function conditionalRelations12<T, U>(x: T extends string ? T : never, y: T, z: T extends string ? U : never): void;
995+
type ElementType = {
996+
type: "text";
997+
text: string;
998+
} | {
999+
type: "image";
1000+
src: string;
1001+
};
1002+
interface Page {
1003+
addElement<T extends ElementType["type"] | (string & {})>(attrs: {
1004+
type: T;
1005+
} & (T extends ElementType["type"] ? Partial<Omit<Extract<ElementType, {
1006+
type: T;
1007+
}>, "type">> : Record<string, any>)): void;
1008+
}
1009+
declare const returned: {
1010+
addElement<T extends ElementType["type"] | (string & {})>(attrs: {
1011+
type: T;
1012+
} & (T extends ElementType["type"] ? Partial<Omit<Extract<ElementType, {
1013+
type: T;
1014+
}>, "type">> : Record<string, any>)): void;
1015+
};
1016+
declare const page: Page;
1017+
declare let f: <A>(m: A, r: (a: A extends string ? A : 0) => void) => void;
1018+
declare let g: <A>(m: A, r: (a: A extends string ? A : 0) => void) => void;

0 commit comments

Comments
 (0)