Skip to content

Commit ddf4c06

Browse files
committedSep 6, 2019
Introduce structural tag T types
1 parent fb453f8 commit ddf4c06

29 files changed

+2683
-387
lines changed
 

Diff for: ‎src/compiler/checker.ts

+63-4
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,7 @@ namespace ts {
631631
const literalTypes = createMap<LiteralType>();
632632
const indexedAccessTypes = createMap<IndexedAccessType>();
633633
const substitutionTypes = createMap<SubstitutionType>();
634+
const structuralTags = createMap<StructuralTagType>();
634635
const evolvingArrayTypes: EvolvingArrayType[] = [];
635636
const undefinedProperties = createMap<Symbol>() as UnderscoreEscapedMap<Symbol>;
636637

@@ -3799,6 +3800,10 @@ namespace ts {
37993800
if (type.flags & TypeFlags.Substitution) {
38003801
return typeToTypeNodeHelper((<SubstitutionType>type).typeVariable, context);
38013802
}
3803+
if (type.flags & TypeFlags.StructuralTag) {
3804+
context.approximateLength += 4;
3805+
return createTypeOperatorNode(SyntaxKind.TagKeyword, typeToTypeNodeHelper((type as StructuralTagType).type, context));
3806+
}
38023807

38033808
return Debug.fail("Should be unreachable.");
38043809

@@ -8103,6 +8108,9 @@ namespace ts {
81038108
if (t.flags & TypeFlags.Substitution) {
81048109
return getBaseConstraint((<SubstitutionType>t).substitute);
81058110
}
8111+
if (t.flags & TypeFlags.StructuralTag) {
8112+
return unknownType;
8113+
}
81068114
return t;
81078115
}
81088116
}
@@ -9924,10 +9932,10 @@ namespace ts {
99249932
return links.resolvedType;
99259933
}
99269934

9927-
function addTypeToIntersection(typeSet: Map<Type>, includes: TypeFlags, type: Type) {
9935+
function addTypeToIntersection(typeSet: Map<Type>, includes: TypeFlags, type: Type, tagSet: Map<StructuralTagType>) {
99289936
const flags = type.flags;
99299937
if (flags & TypeFlags.Intersection) {
9930-
return addTypesToIntersection(typeSet, includes, (<IntersectionType>type).types);
9938+
return addTypesToIntersection(typeSet, includes, (<IntersectionType>type).types, tagSet);
99319939
}
99329940
if (isEmptyAnonymousObjectType(type)) {
99339941
if (!(includes & TypeFlags.IncludesEmptyObject)) {
@@ -9939,6 +9947,9 @@ namespace ts {
99399947
if (flags & TypeFlags.AnyOrUnknown) {
99409948
if (type === wildcardType) includes |= TypeFlags.IncludesWildcard;
99419949
}
9950+
else if (flags & TypeFlags.StructuralTag) {
9951+
tagSet.set(type.id.toString(), type as StructuralTagType);
9952+
}
99429953
else if ((strictNullChecks || !(flags & TypeFlags.Nullable)) && !typeSet.has(type.id.toString())) {
99439954
if (type.flags & TypeFlags.Unit && includes & TypeFlags.Unit) {
99449955
// We have seen two distinct unit types which means we should reduce to an
@@ -9954,9 +9965,23 @@ namespace ts {
99549965

99559966
// Add the given types to the given type set. Order is preserved, freshness is removed from literal
99569967
// types, duplicates are removed, and nested types of the given kind are flattened into the set.
9957-
function addTypesToIntersection(typeSet: Map<Type>, includes: TypeFlags, types: ReadonlyArray<Type>) {
9968+
function addTypesToIntersection(typeSet: Map<Type>, includes: TypeFlags, types: ReadonlyArray<Type>, tagSet?: Map<StructuralTagType> | undefined) {
9969+
const isTopLevel = !tagSet;
9970+
tagSet = tagSet || createMap();
99589971
for (const type of types) {
9959-
includes = addTypeToIntersection(typeSet, includes, getRegularTypeOfLiteralType(type));
9972+
includes = addTypeToIntersection(typeSet, includes, getRegularTypeOfLiteralType(type), tagSet);
9973+
}
9974+
if (isTopLevel && tagSet.size) {
9975+
let tag: StructuralTagType;
9976+
if (tagSet.size === 1) {
9977+
tag = tagSet.values().next().value;
9978+
}
9979+
else {
9980+
const tagTypes: Type[] = [];
9981+
tagSet.forEach(t => tagTypes.push(t.type));
9982+
tag = getStructuralTagForType(getIntersectionType(tagTypes));
9983+
}
9984+
typeSet.set(tag.id.toString(), tag);
99609985
}
99619986
return includes;
99629987
}
@@ -10258,6 +10283,11 @@ namespace ts {
1025810283
case SyntaxKind.ReadonlyKeyword:
1025910284
links.resolvedType = getTypeFromTypeNode(node.type);
1026010285
break;
10286+
case SyntaxKind.TagKeyword:
10287+
const aliasSymbol = getAliasSymbolForTypeNode(node);
10288+
const aliasParams = getTypeArgumentsForAliasSymbol(aliasSymbol);
10289+
links.resolvedType = getStructuralTagForType(getTypeFromTypeNode(node.type), aliasSymbol, aliasParams);
10290+
break;
1026110291
default:
1026210292
throw Debug.assertNever(node.operator);
1026310293
}
@@ -10272,6 +10302,19 @@ namespace ts {
1027210302
return type;
1027310303
}
1027410304

10305+
function getStructuralTagForType(type: Type, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]) {
10306+
const tid = "" + getTypeId(type);
10307+
if (structuralTags.has(tid)) {
10308+
return structuralTags.get(tid)!;
10309+
}
10310+
const tag = createType(TypeFlags.StructuralTag) as StructuralTagType;
10311+
tag.type = type;
10312+
tag.aliasSymbol = aliasSymbol;
10313+
tag.aliasTypeArguments = aliasTypeArguments;
10314+
structuralTags.set(tid, tag);
10315+
return tag;
10316+
}
10317+
1027510318
/**
1027610319
* Returns if a type is or consists of a JSLiteral object type
1027710320
* In addition to objects which are directly literals,
@@ -11706,6 +11749,10 @@ namespace ts {
1170611749
return sub;
1170711750
}
1170811751
}
11752+
if (flags & TypeFlags.StructuralTag) {
11753+
const newType = instantiateType((type as StructuralTagType).type, mapper);
11754+
return newType !== type ? getStructuralTagForType(newType, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)) : type;
11755+
}
1170911756
return type;
1171011757
}
1171111758

@@ -13422,6 +13469,9 @@ namespace ts {
1342213469
if (flags & TypeFlags.Substitution) {
1342313470
return isRelatedTo((<SubstitutionType>source).substitute, (<SubstitutionType>target).substitute, /*reportErrors*/ false);
1342413471
}
13472+
if (flags & TypeFlags.StructuralTag) {
13473+
return isRelatedTo((<StructuralTagType>source).type, (<StructuralTagType>target).type, /*reportErrors*/ false);
13474+
}
1342513475
return Ternary.False;
1342613476
}
1342713477

@@ -13524,6 +13574,12 @@ namespace ts {
1352413574
}
1352513575
}
1352613576
}
13577+
else if (target.flags & TypeFlags.StructuralTag) {
13578+
if (source.flags & TypeFlags.StructuralTag) {
13579+
return isRelatedTo((source as StructuralTagType).type, (target as StructuralTagType).type, reportErrors);
13580+
}
13581+
return Ternary.False;
13582+
}
1352713583

1352813584
if (source.flags & TypeFlags.TypeVariable) {
1352913585
if (source.flags & TypeFlags.IndexedAccess && target.flags & TypeFlags.IndexedAccess) {
@@ -15727,6 +15783,9 @@ namespace ts {
1572715783
inferFromTypes((<IndexType>source).type, (<IndexType>target).type);
1572815784
contravariant = !contravariant;
1572915785
}
15786+
else if (source.flags & TypeFlags.StructuralTag && target.flags & TypeFlags.StructuralTag) {
15787+
inferFromTypes((<StructuralTagType>source).type, (<StructuralTagType>target).type);
15788+
}
1573015789
else if ((isLiteralType(source) || source.flags & TypeFlags.String) && target.flags & TypeFlags.Index) {
1573115790
const empty = createEmptyObjectTypeFromStringLiteral(source);
1573215791
contravariant = !contravariant;

Diff for: ‎src/compiler/factory.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -876,8 +876,8 @@ namespace ts {
876876
}
877877

878878
export function createTypeOperatorNode(type: TypeNode): TypeOperatorNode;
879-
export function createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword, type: TypeNode): TypeOperatorNode;
880-
export function createTypeOperatorNode(operatorOrType: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword | TypeNode, type?: TypeNode) {
879+
export function createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword | SyntaxKind.TagKeyword, type: TypeNode): TypeOperatorNode;
880+
export function createTypeOperatorNode(operatorOrType: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword | SyntaxKind.TagKeyword | TypeNode, type?: TypeNode) {
881881
const node = createSynthesizedNode(SyntaxKind.TypeOperator) as TypeOperatorNode;
882882
node.operator = typeof operatorOrType === "number" ? operatorOrType : SyntaxKind.KeyOfKeyword;
883883
node.type = parenthesizeElementTypeMember(typeof operatorOrType === "number" ? type! : operatorOrType);

Diff for: ‎src/compiler/parser.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -3053,6 +3053,7 @@ namespace ts {
30533053
case SyntaxKind.ReadonlyKeyword:
30543054
case SyntaxKind.SymbolKeyword:
30553055
case SyntaxKind.UniqueKeyword:
3056+
case SyntaxKind.TagKeyword:
30563057
case SyntaxKind.VoidKeyword:
30573058
case SyntaxKind.UndefinedKeyword:
30583059
case SyntaxKind.NullKeyword:
@@ -3140,7 +3141,7 @@ namespace ts {
31403141
return finishNode(postfix);
31413142
}
31423143

3143-
function parseTypeOperator(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword) {
3144+
function parseTypeOperator(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword | SyntaxKind.TagKeyword) {
31443145
const node = <TypeOperatorNode>createNode(SyntaxKind.TypeOperator);
31453146
parseExpected(operator);
31463147
node.operator = operator;
@@ -3163,6 +3164,7 @@ namespace ts {
31633164
case SyntaxKind.KeyOfKeyword:
31643165
case SyntaxKind.UniqueKeyword:
31653166
case SyntaxKind.ReadonlyKeyword:
3167+
case SyntaxKind.TagKeyword:
31663168
return parseTypeOperator(operator);
31673169
case SyntaxKind.InferKeyword:
31683170
return parseInferType();

Diff for: ‎src/compiler/scanner.ts

+1
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ namespace ts {
138138
async: SyntaxKind.AsyncKeyword,
139139
await: SyntaxKind.AwaitKeyword,
140140
of: SyntaxKind.OfKeyword,
141+
tag: SyntaxKind.TagKeyword,
141142
};
142143

143144
const textToKeyword = createMapFromTemplate(textToKeywordObj);

Diff for: ‎src/compiler/types.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ namespace ts {
103103
| SyntaxKind.YieldKeyword
104104
| SyntaxKind.AsyncKeyword
105105
| SyntaxKind.AwaitKeyword
106+
| SyntaxKind.TagKeyword
106107
| SyntaxKind.OfKeyword;
107108

108109
export type JsxTokenSyntaxKind =
@@ -277,6 +278,7 @@ namespace ts {
277278
FromKeyword,
278279
GlobalKeyword,
279280
BigIntKeyword,
281+
TagKeyword,
280282
OfKeyword, // LastKeyword and LastToken and LastContextualKeyword
281283

282284
// Parse tree nodes
@@ -1246,7 +1248,7 @@ namespace ts {
12461248

12471249
export interface TypeOperatorNode extends TypeNode {
12481250
kind: SyntaxKind.TypeOperator;
1249-
operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword;
1251+
operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword | SyntaxKind.TagKeyword;
12501252
type: TypeNode;
12511253
}
12521254

@@ -3984,6 +3986,7 @@ namespace ts {
39843986
Conditional = 1 << 24, // T extends U ? X : Y
39853987
Substitution = 1 << 25, // Type parameter substitution
39863988
NonPrimitive = 1 << 26, // intrinsic object type
3989+
StructuralTag = 1 << 27, // tag T
39873990

39883991
/* @internal */
39893992
AnyOrUnknown = Any | Unknown,
@@ -4013,7 +4016,7 @@ namespace ts {
40134016
UnionOrIntersection = Union | Intersection,
40144017
StructuredType = Object | Union | Intersection,
40154018
TypeVariable = TypeParameter | IndexedAccess,
4016-
InstantiableNonPrimitive = TypeVariable | Conditional | Substitution,
4019+
InstantiableNonPrimitive = TypeVariable | Conditional | Substitution | StructuralTag,
40174020
InstantiablePrimitive = Index,
40184021
Instantiable = InstantiableNonPrimitive | InstantiablePrimitive,
40194022
StructuredOrInstantiable = StructuredType | Instantiable,
@@ -4431,6 +4434,11 @@ namespace ts {
44314434
substitute: Type; // Type to substitute for type parameter
44324435
}
44334436

4437+
// Structual tag type, or a `tag T` (TypeFlags.StructuralTag)
4438+
export interface StructuralTagType extends InstantiableType {
4439+
type: Type;
4440+
}
4441+
44344442
/* @internal */
44354443
export const enum JsxReferenceKind {
44364444
Component,

0 commit comments

Comments
 (0)