Skip to content

Commit d91d6e0

Browse files
michaelfigcodex
andcommitted
Relate deferred registered symbols through their keys
Add regression coverage for generic key relations and symbol union and intersection reduction. Co-authored-by: Codex <codex@openai.com>
1 parent c6d0ad8 commit d91d6e0

7 files changed

Lines changed: 242 additions & 6 deletions

File tree

‎packages/typescript/src/enums/typeFlags.enum.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export enum TypeFlags {
6464
ObjectFlagsType = Any | Nullable | Never | Object | Union | Intersection,
6565
Simplifiable = IndexedAccess | Conditional | Index,
6666
Singleton = Any | Unknown | String | Number | Boolean | BigInt | ESSymbol | Void | Undefined | Null | Never | NonPrimitive,
67-
Narrowable = Any | Unknown | StructuredOrInstantiable | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbol | UniqueESSymbol | NonPrimitive,
67+
Narrowable = Any | Unknown | StructuredOrInstantiable | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbolLike | NonPrimitive,
6868
IncludesMask = Any | Unknown | Primitive | Never | Object | Union | Intersection | NonPrimitive | TemplateLiteral | StringMapping | RegisteredESSymbol,
6969
IncludesMissingType = TypeParameter,
7070
IncludesNonWideningType = Index,

‎tsc/internal/checker/checker.go‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26156,7 +26156,7 @@ func (c *Checker) getUnionTypeWorker(types []*Type, unionReduction UnionReductio
2615626156
typeSet = slices.Delete(typeSet, 1, 2)
2615726157
}
2615826158
}
26159-
if includes&(TypeFlagsEnum|TypeFlagsLiteral|TypeFlagsUniqueESSymbol|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 ||
26159+
if includes&(TypeFlagsEnum|TypeFlagsLiteral|TypeFlagsUniqueESSymbol|TypeFlagsRegisteredESSymbol|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 ||
2616026160
includes&TypeFlagsVoid != 0 && includes&TypeFlagsUndefined != 0 {
2616126161
typeSet = c.removeRedundantLiteralTypes(typeSet, includes, unionReduction&UnionReductionSubtype != 0)
2616226162
}
@@ -26329,7 +26329,7 @@ func (c *Checker) removeRedundantLiteralTypes(types []*Type, includes TypeFlags,
2632926329
remove := flags&(TypeFlagsStringLiteral|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 && includes&TypeFlagsString != 0 ||
2633026330
flags&TypeFlagsNumberLiteral != 0 && includes&TypeFlagsNumber != 0 ||
2633126331
flags&TypeFlagsBigIntLiteral != 0 && includes&TypeFlagsBigInt != 0 ||
26332-
flags&TypeFlagsUniqueESSymbol != 0 && includes&TypeFlagsESSymbol != 0 ||
26332+
flags&(TypeFlagsUniqueESSymbol|TypeFlagsRegisteredESSymbol) != 0 && includes&TypeFlagsESSymbol != 0 ||
2633326333
reduceVoidUndefined && flags&TypeFlagsUndefined != 0 && includes&TypeFlagsVoid != 0 ||
2633426334
isFreshLiteralType(t) && containsType(types, t.AsLiteralType().regularType)
2633526335
if remove {
@@ -26596,7 +26596,7 @@ func (c *Checker) getIntersectionTypeEx(types []*Type, flags IntersectionFlags,
2659626596
if includes&TypeFlagsString != 0 && includes&(TypeFlagsStringLiteral|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 ||
2659726597
includes&TypeFlagsNumber != 0 && includes&TypeFlagsNumberLiteral != 0 ||
2659826598
includes&TypeFlagsBigInt != 0 && includes&TypeFlagsBigIntLiteral != 0 ||
26599-
includes&TypeFlagsESSymbol != 0 && includes&TypeFlagsUniqueESSymbol != 0 ||
26599+
includes&TypeFlagsESSymbol != 0 && includes&(TypeFlagsUniqueESSymbol|TypeFlagsRegisteredESSymbol) != 0 ||
2660026600
includes&TypeFlagsVoid != 0 && includes&TypeFlagsUndefined != 0 ||
2660126601
includes&TypeFlagsIncludesEmptyObject != 0 && includes&TypeFlagsDefinitelyNonNullable != 0 {
2660226602
if flags&IntersectionFlagsNoSupertypeReduction == 0 {
@@ -26785,7 +26785,7 @@ func (c *Checker) removeRedundantSupertypes(types []*Type, includes TypeFlags) [
2678526785
remove := t.flags&TypeFlagsString != 0 && includes&(TypeFlagsStringLiteral|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 ||
2678626786
t.flags&TypeFlagsNumber != 0 && includes&TypeFlagsNumberLiteral != 0 ||
2678726787
t.flags&TypeFlagsBigInt != 0 && includes&TypeFlagsBigIntLiteral != 0 ||
26788-
t.flags&TypeFlagsESSymbol != 0 && includes&TypeFlagsUniqueESSymbol != 0 ||
26788+
t.flags&TypeFlagsESSymbol != 0 && includes&(TypeFlagsUniqueESSymbol|TypeFlagsRegisteredESSymbol) != 0 ||
2678926789
t.flags&TypeFlagsVoid != 0 && includes&TypeFlagsUndefined != 0 ||
2679026790
c.IsEmptyAnonymousObjectType(t) && includes&TypeFlagsDefinitelyNonNullable != 0
2679126791
if remove {
@@ -28033,6 +28033,10 @@ func (c *Checker) computeBaseConstraint(t *Type, stack []RecursionId) *Type {
2803328033
}
2803428034
return c.stringType
2803528035
case t.flags&TypeFlagsRegisteredESSymbol != 0:
28036+
constraint := c.getNextBaseConstraint(t.Target(), stack)
28037+
if constraint != nil && constraint != t.Target() {
28038+
return c.getRegisteredESSymbolType(constraint, t.alias)
28039+
}
2803628040
return c.esSymbolType
2803728041
case t.flags&TypeFlagsIndexedAccess != 0:
2803828042
if c.isMappedTypeGenericIndexedAccess(t) {

‎tsc/internal/checker/relater.go‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3831,6 +3831,21 @@ func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, repo
38313831
}
38323832
}
38333833
}
3834+
case source.flags&TypeFlagsRegisteredESSymbol != 0:
3835+
if target.flags&TypeFlagsRegisteredESSymbol != 0 {
3836+
result = r.isRelatedTo(source.Target(), target.Target(), RecursionFlagsBoth, reportErrors)
3837+
if result != TernaryFalse {
3838+
return result
3839+
}
3840+
} else {
3841+
constraint := r.c.getBaseConstraintOfType(source)
3842+
if constraint != nil {
3843+
result = r.isRelatedTo(constraint, target, RecursionFlagsSource, reportErrors)
3844+
if result != TernaryFalse {
3845+
return result
3846+
}
3847+
}
3848+
}
38343849
default:
38353850
// An empty object type is related to any mapped type that includes a '?' modifier.
38363851
if r.relation != r.c.subtypeRelation && r.relation != r.c.strictSubtypeRelation && isPartialMappedType(target) && r.c.isEmptyObjectType(source) {

‎tsc/internal/checker/types.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ const (
503503
TypeFlagsSingleton = TypeFlagsAny | TypeFlagsUnknown | TypeFlagsString | TypeFlagsNumber | TypeFlagsBoolean | TypeFlagsBigInt | TypeFlagsESSymbol | TypeFlagsVoid | TypeFlagsUndefined | TypeFlagsNull | TypeFlagsNever | TypeFlagsNonPrimitive
504504
// 'TypeFlagsNarrowable' types are types where narrowing actually narrows.
505505
// This *should* be every type other than null, undefined, void, and never
506-
TypeFlagsNarrowable = TypeFlagsAny | TypeFlagsUnknown | TypeFlagsStructuredOrInstantiable | TypeFlagsStringLike | TypeFlagsNumberLike | TypeFlagsBigIntLike | TypeFlagsBooleanLike | TypeFlagsESSymbol | TypeFlagsUniqueESSymbol | TypeFlagsNonPrimitive
506+
TypeFlagsNarrowable = TypeFlagsAny | TypeFlagsUnknown | TypeFlagsStructuredOrInstantiable | TypeFlagsStringLike | TypeFlagsNumberLike | TypeFlagsBigIntLike | TypeFlagsBooleanLike | TypeFlagsESSymbolLike | TypeFlagsNonPrimitive
507507
// The following flags are aggregated during union and intersection type construction
508508
TypeFlagsIncludesMask = TypeFlagsAny | TypeFlagsUnknown | TypeFlagsPrimitive | TypeFlagsNever | TypeFlagsObject | TypeFlagsUnion | TypeFlagsIntersection | TypeFlagsNonPrimitive | TypeFlagsTemplateLiteral | TypeFlagsStringMapping | TypeFlagsRegisteredESSymbol
509509
// The following flags are used for different purposes during union and intersection type construction
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
//// [tests/cases/compiler/registeredSymbolGenericRelations.ts] ////
2+
3+
=== registeredSymbolGenericRelations.ts ===
4+
type Equal<A, B> =
5+
>Equal : Symbol(Equal, Decl(registeredSymbolGenericRelations.ts, 0, 0))
6+
>A : Symbol(A, Decl(registeredSymbolGenericRelations.ts, 0, 11))
7+
>B : Symbol(B, Decl(registeredSymbolGenericRelations.ts, 0, 13))
8+
9+
(<T>() => T extends A ? 1 : 2) extends
10+
>T : Symbol(T, Decl(registeredSymbolGenericRelations.ts, 1, 6))
11+
>T : Symbol(T, Decl(registeredSymbolGenericRelations.ts, 1, 6))
12+
>A : Symbol(A, Decl(registeredSymbolGenericRelations.ts, 0, 11))
13+
14+
(<T>() => T extends B ? 1 : 2) ? true : false;
15+
>T : Symbol(T, Decl(registeredSymbolGenericRelations.ts, 2, 6))
16+
>T : Symbol(T, Decl(registeredSymbolGenericRelations.ts, 2, 6))
17+
>B : Symbol(B, Decl(registeredSymbolGenericRelations.ts, 0, 13))
18+
19+
declare function inferKey<K extends string>(value: RegisteredSymbol<K>): K;
20+
>inferKey : Symbol(inferKey, Decl(registeredSymbolGenericRelations.ts, 2, 50))
21+
>K : Symbol(K, Decl(registeredSymbolGenericRelations.ts, 4, 26))
22+
>value : Symbol(value, Decl(registeredSymbolGenericRelations.ts, 4, 44))
23+
>RegisteredSymbol : Symbol(RegisteredSymbol, Decl(lib.es2015.symbol.d.ts, --, --))
24+
>K : Symbol(K, Decl(registeredSymbolGenericRelations.ts, 4, 26))
25+
>K : Symbol(K, Decl(registeredSymbolGenericRelations.ts, 4, 26))
26+
27+
const inferred: "foo" = inferKey(Symbol.for("foo"));
28+
>inferred : Symbol(inferred, Decl(registeredSymbolGenericRelations.ts, 5, 5))
29+
>inferKey : Symbol(inferKey, Decl(registeredSymbolGenericRelations.ts, 2, 50))
30+
>Symbol.for : Symbol(SymbolConstructor.for, Decl(lib.es2015.symbol.d.ts, --, --))
31+
>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
32+
>for : Symbol(SymbolConstructor.for, Decl(lib.es2015.symbol.d.ts, --, --))
33+
34+
function simplify<K extends string>() {
35+
>simplify : Symbol(simplify, Decl(registeredSymbolGenericRelations.ts, 5, 52))
36+
>K : Symbol(K, Decl(registeredSymbolGenericRelations.ts, 7, 18))
37+
38+
const unionIsSymbol: true = null! as Equal<RegisteredSymbol<K> | symbol, symbol>;
39+
>unionIsSymbol : Symbol(unionIsSymbol, Decl(registeredSymbolGenericRelations.ts, 8, 9))
40+
>Equal : Symbol(Equal, Decl(registeredSymbolGenericRelations.ts, 0, 0))
41+
>RegisteredSymbol : Symbol(RegisteredSymbol, Decl(lib.es2015.symbol.d.ts, --, --))
42+
>K : Symbol(K, Decl(registeredSymbolGenericRelations.ts, 7, 18))
43+
44+
const intersectionIsRegistered: true = null! as Equal<RegisteredSymbol<K> & symbol, RegisteredSymbol<K>>;
45+
>intersectionIsRegistered : Symbol(intersectionIsRegistered, Decl(registeredSymbolGenericRelations.ts, 9, 9))
46+
>Equal : Symbol(Equal, Decl(registeredSymbolGenericRelations.ts, 0, 0))
47+
>RegisteredSymbol : Symbol(RegisteredSymbol, Decl(lib.es2015.symbol.d.ts, --, --))
48+
>K : Symbol(K, Decl(registeredSymbolGenericRelations.ts, 7, 18))
49+
>RegisteredSymbol : Symbol(RegisteredSymbol, Decl(lib.es2015.symbol.d.ts, --, --))
50+
>K : Symbol(K, Decl(registeredSymbolGenericRelations.ts, 7, 18))
51+
52+
return { unionIsSymbol, intersectionIsRegistered };
53+
>unionIsSymbol : Symbol(unionIsSymbol, Decl(registeredSymbolGenericRelations.ts, 10, 12))
54+
>intersectionIsRegistered : Symbol(intersectionIsRegistered, Decl(registeredSymbolGenericRelations.ts, 10, 27))
55+
}
56+
57+
function widenKey<K extends "foo" | "bar">(value: RegisteredSymbol<K>): RegisteredSymbol<"foo" | "bar"> {
58+
>widenKey : Symbol(widenKey, Decl(registeredSymbolGenericRelations.ts, 11, 1))
59+
>K : Symbol(K, Decl(registeredSymbolGenericRelations.ts, 13, 18))
60+
>value : Symbol(value, Decl(registeredSymbolGenericRelations.ts, 13, 43))
61+
>RegisteredSymbol : Symbol(RegisteredSymbol, Decl(lib.es2015.symbol.d.ts, --, --))
62+
>K : Symbol(K, Decl(registeredSymbolGenericRelations.ts, 13, 18))
63+
>RegisteredSymbol : Symbol(RegisteredSymbol, Decl(lib.es2015.symbol.d.ts, --, --))
64+
65+
return value;
66+
>value : Symbol(value, Decl(registeredSymbolGenericRelations.ts, 13, 43))
67+
}
68+
69+
function widenGeneric<K extends L, L extends string>(value: RegisteredSymbol<K>): RegisteredSymbol<L> {
70+
>widenGeneric : Symbol(widenGeneric, Decl(registeredSymbolGenericRelations.ts, 15, 1))
71+
>K : Symbol(K, Decl(registeredSymbolGenericRelations.ts, 17, 22))
72+
>L : Symbol(L, Decl(registeredSymbolGenericRelations.ts, 17, 34))
73+
>L : Symbol(L, Decl(registeredSymbolGenericRelations.ts, 17, 34))
74+
>value : Symbol(value, Decl(registeredSymbolGenericRelations.ts, 17, 53))
75+
>RegisteredSymbol : Symbol(RegisteredSymbol, Decl(lib.es2015.symbol.d.ts, --, --))
76+
>K : Symbol(K, Decl(registeredSymbolGenericRelations.ts, 17, 22))
77+
>RegisteredSymbol : Symbol(RegisteredSymbol, Decl(lib.es2015.symbol.d.ts, --, --))
78+
>L : Symbol(L, Decl(registeredSymbolGenericRelations.ts, 17, 34))
79+
80+
return value;
81+
>value : Symbol(value, Decl(registeredSymbolGenericRelations.ts, 17, 53))
82+
}
83+
84+
function commonSymbol<K extends string>(value: RegisteredSymbol<K>): symbol {
85+
>commonSymbol : Symbol(commonSymbol, Decl(registeredSymbolGenericRelations.ts, 19, 1))
86+
>K : Symbol(K, Decl(registeredSymbolGenericRelations.ts, 21, 22))
87+
>value : Symbol(value, Decl(registeredSymbolGenericRelations.ts, 21, 40))
88+
>RegisteredSymbol : Symbol(RegisteredSymbol, Decl(lib.es2015.symbol.d.ts, --, --))
89+
>K : Symbol(K, Decl(registeredSymbolGenericRelations.ts, 21, 22))
90+
91+
return value;
92+
>value : Symbol(value, Decl(registeredSymbolGenericRelations.ts, 21, 40))
93+
}
94+
95+
function intersect<K extends string>(value: RegisteredSymbol<K>): RegisteredSymbol<K> & symbol {
96+
>intersect : Symbol(intersect, Decl(registeredSymbolGenericRelations.ts, 23, 1))
97+
>K : Symbol(K, Decl(registeredSymbolGenericRelations.ts, 25, 19))
98+
>value : Symbol(value, Decl(registeredSymbolGenericRelations.ts, 25, 37))
99+
>RegisteredSymbol : Symbol(RegisteredSymbol, Decl(lib.es2015.symbol.d.ts, --, --))
100+
>K : Symbol(K, Decl(registeredSymbolGenericRelations.ts, 25, 19))
101+
>RegisteredSymbol : Symbol(RegisteredSymbol, Decl(lib.es2015.symbol.d.ts, --, --))
102+
>K : Symbol(K, Decl(registeredSymbolGenericRelations.ts, 25, 19))
103+
104+
return value;
105+
>value : Symbol(value, Decl(registeredSymbolGenericRelations.ts, 25, 37))
106+
}
107+
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//// [tests/cases/compiler/registeredSymbolGenericRelations.ts] ////
2+
3+
=== registeredSymbolGenericRelations.ts ===
4+
type Equal<A, B> =
5+
>Equal : Equal<A, B>
6+
7+
(<T>() => T extends A ? 1 : 2) extends
8+
(<T>() => T extends B ? 1 : 2) ? true : false;
9+
>true : true
10+
>false : false
11+
12+
declare function inferKey<K extends string>(value: RegisteredSymbol<K>): K;
13+
>inferKey : <K extends string>(value: RegisteredSymbol<K>) => K
14+
>value : RegisteredSymbol<K>
15+
16+
const inferred: "foo" = inferKey(Symbol.for("foo"));
17+
>inferred : "foo"
18+
>inferKey(Symbol.for("foo")) : "foo"
19+
>inferKey : <K extends string>(value: RegisteredSymbol<K>) => K
20+
>Symbol.for("foo") : RegisteredSymbol<"foo">
21+
>Symbol.for : <Key extends string>(key: Key) => RegisteredSymbol<Key>
22+
>Symbol : SymbolConstructor
23+
>for : <Key extends string>(key: Key) => RegisteredSymbol<Key>
24+
>"foo" : "foo"
25+
26+
function simplify<K extends string>() {
27+
>simplify : <K extends string>() => { unionIsSymbol: true; intersectionIsRegistered: true; }
28+
29+
const unionIsSymbol: true = null! as Equal<RegisteredSymbol<K> | symbol, symbol>;
30+
>unionIsSymbol : true
31+
>true : true
32+
>null! as Equal<RegisteredSymbol<K> | symbol, symbol> : true
33+
>null! : never
34+
35+
const intersectionIsRegistered: true = null! as Equal<RegisteredSymbol<K> & symbol, RegisteredSymbol<K>>;
36+
>intersectionIsRegistered : true
37+
>true : true
38+
>null! as Equal<RegisteredSymbol<K> & symbol, RegisteredSymbol<K>> : true
39+
>null! : never
40+
41+
return { unionIsSymbol, intersectionIsRegistered };
42+
>{ unionIsSymbol, intersectionIsRegistered } : { unionIsSymbol: true; intersectionIsRegistered: true; }
43+
>unionIsSymbol : true
44+
>intersectionIsRegistered : true
45+
}
46+
47+
function widenKey<K extends "foo" | "bar">(value: RegisteredSymbol<K>): RegisteredSymbol<"foo" | "bar"> {
48+
>widenKey : <K extends "foo" | "bar">(value: RegisteredSymbol<K>) => RegisteredSymbol<"foo" | "bar">
49+
>value : RegisteredSymbol<K>
50+
51+
return value;
52+
>value : RegisteredSymbol<"bar"> | RegisteredSymbol<"foo">
53+
}
54+
55+
function widenGeneric<K extends L, L extends string>(value: RegisteredSymbol<K>): RegisteredSymbol<L> {
56+
>widenGeneric : <K extends L, L extends string>(value: RegisteredSymbol<K>) => RegisteredSymbol<L>
57+
>value : RegisteredSymbol<K>
58+
59+
return value;
60+
>value : RegisteredSymbol<K>
61+
}
62+
63+
function commonSymbol<K extends string>(value: RegisteredSymbol<K>): symbol {
64+
>commonSymbol : <K extends string>(value: RegisteredSymbol<K>) => symbol
65+
>value : RegisteredSymbol<K>
66+
67+
return value;
68+
>value : RegisteredSymbol<K>
69+
}
70+
71+
function intersect<K extends string>(value: RegisteredSymbol<K>): RegisteredSymbol<K> & symbol {
72+
>intersect : <K extends string>(value: RegisteredSymbol<K>) => RegisteredSymbol<K> & symbol
73+
>value : RegisteredSymbol<K>
74+
75+
return value;
76+
>value : RegisteredSymbol<K>
77+
}
78+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// @target: es2015
2+
// @strict: true
3+
// @noEmit: true
4+
5+
type Equal<A, B> =
6+
(<T>() => T extends A ? 1 : 2) extends
7+
(<T>() => T extends B ? 1 : 2) ? true : false;
8+
9+
declare function inferKey<K extends string>(value: RegisteredSymbol<K>): K;
10+
const inferred: "foo" = inferKey(Symbol.for("foo"));
11+
12+
function simplify<K extends string>() {
13+
const unionIsSymbol: true = null! as Equal<RegisteredSymbol<K> | symbol, symbol>;
14+
const intersectionIsRegistered: true = null! as Equal<RegisteredSymbol<K> & symbol, RegisteredSymbol<K>>;
15+
return { unionIsSymbol, intersectionIsRegistered };
16+
}
17+
18+
function widenKey<K extends "foo" | "bar">(value: RegisteredSymbol<K>): RegisteredSymbol<"foo" | "bar"> {
19+
return value;
20+
}
21+
22+
function widenGeneric<K extends L, L extends string>(value: RegisteredSymbol<K>): RegisteredSymbol<L> {
23+
return value;
24+
}
25+
26+
function commonSymbol<K extends string>(value: RegisteredSymbol<K>): symbol {
27+
return value;
28+
}
29+
30+
function intersect<K extends string>(value: RegisteredSymbol<K>): RegisteredSymbol<K> & symbol {
31+
return value;
32+
}

0 commit comments

Comments
 (0)