Skip to content

Commit

Permalink
Preserve aliases in tags better, add global Tag alias, distribute tag…
Browse files Browse the repository at this point in the history
…s into unions
  • Loading branch information
weswigham committed Sep 9, 2019
1 parent f37aeab commit b38d95f
Show file tree
Hide file tree
Showing 22 changed files with 953 additions and 103 deletions.
44 changes: 40 additions & 4 deletions src/compiler/checker.ts
Expand Up @@ -631,7 +631,7 @@ namespace ts {
const literalTypes = createMap<LiteralType>();
const indexedAccessTypes = createMap<IndexedAccessType>();
const substitutionTypes = createMap<SubstitutionType>();
const structuralTags = createMap<StructuralTagType>();
const structuralTags = createMap<Type>();
const evolvingArrayTypes: EvolvingArrayType[] = [];
const undefinedProperties = createMap<Symbol>() as UnderscoreEscapedMap<Symbol>;

Expand Down Expand Up @@ -3801,8 +3801,33 @@ namespace ts {
return typeToTypeNodeHelper((<SubstitutionType>type).typeVariable, context);
}
if (type.flags & TypeFlags.StructuralTag) {
const innerType = (type as StructuralTagType).type;
if (innerType.flags & TypeFlags.Intersection) {
// If some inner type of the intersection has an alias when hoisted out, (attempt to) hoist out all of the aliasable things
if (some((innerType as IntersectionType).types, t => !!getStructuralTagForType(t).aliasSymbol)) {
const aliasingTypes: Type[] = [];
const nonAliasingTypes: Type[] = [];
for (const t of (innerType as IntersectionType).types) {
const taggedVersion = getStructuralTagForType(t);
if (taggedVersion.aliasSymbol && (context.flags & NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope || isTypeSymbolAccessible(taggedVersion.aliasSymbol, context.enclosingDeclaration))) {
aliasingTypes.push(taggedVersion);
}
else {
nonAliasingTypes.push(t);
}
}
if (length(aliasingTypes)) {
if (length(nonAliasingTypes)) {
aliasingTypes.push(getStructuralTagForType(getIntersectionType(nonAliasingTypes)));
}
// Do note: this can make an intersection become nested within another intersection - this _is_ handled correctly
// during emit, so is fine (and honestly is more clear, since it groups all the tags together)
return createUnionOrIntersectionTypeNode(SyntaxKind.IntersectionType, map(aliasingTypes, t => typeToTypeNodeHelper(t, context)));
}
}
}
context.approximateLength += 4;
return createTypeOperatorNode(SyntaxKind.TagKeyword, typeToTypeNodeHelper((type as StructuralTagType).type, context));
return createTypeOperatorNode(SyntaxKind.TagKeyword, typeToTypeNodeHelper(innerType, context));
}

return Debug.fail("Should be unreachable.");
Expand Down Expand Up @@ -9980,14 +10005,17 @@ namespace ts {
includes = addTypeToIntersection(typeSet, includes, getRegularTypeOfLiteralType(type), tagSet);
}
if (isTopLevel && tagSet.size) {
let tag: StructuralTagType;
let tag: Type;
if (tagSet.size === 1) {
tag = tagSet.values().next().value;
}
else {
const tagTypes: Type[] = [];
tagSet.forEach(t => tagTypes.push(t.type));
tag = getStructuralTagForType(getIntersectionType(tagTypes));
if (tag.flags & TypeFlags.Union) {
includes |= TypeFlags.Union;
}
}
typeSet.set(tag.id.toString(), tag);
}
Expand Down Expand Up @@ -10310,11 +10338,19 @@ namespace ts {
return type;
}

function getStructuralTagForType(type: Type, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]) {
function getStructuralTagForType(type: Type, aliasSymbol?: Symbol | number, aliasTypeArguments?: readonly Type[]) {
if (typeof aliasSymbol === "number") {
aliasSymbol = undefined;
}
const tid = "" + getTypeId(type);
if (structuralTags.has(tid)) {
return structuralTags.get(tid)!;
}
if (type.flags & TypeFlags.Union) {
const union = getUnionType(map((type as UnionType).types, getStructuralTagForType), UnionReduction.Subtype, aliasSymbol, aliasTypeArguments);
structuralTags.set(tid, union);
return union;
}
const tag = createType(TypeFlags.StructuralTag) as StructuralTagType;
tag.type = type;
tag.aliasSymbol = aliasSymbol;
Expand Down
1 change: 1 addition & 0 deletions src/harness/fourslash.ts
Expand Up @@ -4672,6 +4672,7 @@ namespace FourSlashInterface {
typeEntry("ReturnType"),
typeEntry("InstanceType"),
interfaceEntry("ThisType"),
typeEntry("Tag"),
varEntry("ArrayBuffer"),
interfaceEntry("ArrayBufferTypes"),
typeEntry("ArrayBufferLike"),
Expand Down
7 changes: 7 additions & 0 deletions src/lib/es5.d.ts
Expand Up @@ -1478,6 +1478,13 @@ type InstanceType<T extends new (...args: any) => any> = T extends new (...args:
*/
interface ThisType<T> { }

/**
* Constructs a structural tag over property name(s) `K`
*/
type Tag<K extends keyof any> = tag {
[_ in K]: void;
};

/**
* Represents a raw buffer of binary data, which is used to store data for the
* different typed arrays. ArrayBuffers cannot be read from or written to directly,
Expand Down
10 changes: 5 additions & 5 deletions tests/baselines/reference/numericLiteralTypes1.js
Expand Up @@ -66,17 +66,17 @@ function assertNever(x: never): never {
throw new Error("Unexpected value");
}

type Tag = 0 | 1 | 2;
type NumericTag = 0 | 1 | 2;

function f10(x: Tag) {
function f10(x: NumericTag) {
switch (x) {
case 0: return "a";
case 1: return "b";
case 2: return "c";
}
}

function f11(x: Tag) {
function f11(x: NumericTag) {
switch (x) {
case 0: return "a";
case 1: return "b";
Expand All @@ -85,7 +85,7 @@ function f11(x: Tag) {
return assertNever(x);
}

function f12(x: Tag) {
function f12(x: NumericTag) {
if (x) {
x;
}
Expand All @@ -94,7 +94,7 @@ function f12(x: Tag) {
}
}

function f13(x: Tag) {
function f13(x: NumericTag) {
if (x === 0 || x === 2) {
x;
}
Expand Down
22 changes: 11 additions & 11 deletions tests/baselines/reference/numericLiteralTypes1.symbols
Expand Up @@ -221,13 +221,13 @@ function assertNever(x: never): never {
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
}

type Tag = 0 | 1 | 2;
>Tag : Symbol(Tag, Decl(numericLiteralTypes1.ts, 65, 1))
type NumericTag = 0 | 1 | 2;
>NumericTag : Symbol(NumericTag, Decl(numericLiteralTypes1.ts, 65, 1))

function f10(x: Tag) {
>f10 : Symbol(f10, Decl(numericLiteralTypes1.ts, 67, 21))
function f10(x: NumericTag) {
>f10 : Symbol(f10, Decl(numericLiteralTypes1.ts, 67, 28))
>x : Symbol(x, Decl(numericLiteralTypes1.ts, 69, 13))
>Tag : Symbol(Tag, Decl(numericLiteralTypes1.ts, 65, 1))
>NumericTag : Symbol(NumericTag, Decl(numericLiteralTypes1.ts, 65, 1))

switch (x) {
>x : Symbol(x, Decl(numericLiteralTypes1.ts, 69, 13))
Expand All @@ -238,10 +238,10 @@ function f10(x: Tag) {
}
}

function f11(x: Tag) {
function f11(x: NumericTag) {
>f11 : Symbol(f11, Decl(numericLiteralTypes1.ts, 75, 1))
>x : Symbol(x, Decl(numericLiteralTypes1.ts, 77, 13))
>Tag : Symbol(Tag, Decl(numericLiteralTypes1.ts, 65, 1))
>NumericTag : Symbol(NumericTag, Decl(numericLiteralTypes1.ts, 65, 1))

switch (x) {
>x : Symbol(x, Decl(numericLiteralTypes1.ts, 77, 13))
Expand All @@ -255,10 +255,10 @@ function f11(x: Tag) {
>x : Symbol(x, Decl(numericLiteralTypes1.ts, 77, 13))
}

function f12(x: Tag) {
function f12(x: NumericTag) {
>f12 : Symbol(f12, Decl(numericLiteralTypes1.ts, 84, 1))
>x : Symbol(x, Decl(numericLiteralTypes1.ts, 86, 13))
>Tag : Symbol(Tag, Decl(numericLiteralTypes1.ts, 65, 1))
>NumericTag : Symbol(NumericTag, Decl(numericLiteralTypes1.ts, 65, 1))

if (x) {
>x : Symbol(x, Decl(numericLiteralTypes1.ts, 86, 13))
Expand All @@ -272,10 +272,10 @@ function f12(x: Tag) {
}
}

function f13(x: Tag) {
function f13(x: NumericTag) {
>f13 : Symbol(f13, Decl(numericLiteralTypes1.ts, 93, 1))
>x : Symbol(x, Decl(numericLiteralTypes1.ts, 95, 13))
>Tag : Symbol(Tag, Decl(numericLiteralTypes1.ts, 65, 1))
>NumericTag : Symbol(NumericTag, Decl(numericLiteralTypes1.ts, 65, 1))

if (x === 0 || x === 2) {
>x : Symbol(x, Decl(numericLiteralTypes1.ts, 95, 13))
Expand Down
12 changes: 6 additions & 6 deletions tests/baselines/reference/numericLiteralTypes1.types
Expand Up @@ -259,10 +259,10 @@ function assertNever(x: never): never {
>"Unexpected value" : "Unexpected value"
}

type Tag = 0 | 1 | 2;
>Tag : 0 | 1 | 2
type NumericTag = 0 | 1 | 2;
>NumericTag : 0 | 1 | 2

function f10(x: Tag) {
function f10(x: NumericTag) {
>f10 : (x: 0 | 1 | 2) => "a" | "b" | "c"
>x : 0 | 1 | 2

Expand All @@ -283,7 +283,7 @@ function f10(x: Tag) {
}
}

function f11(x: Tag) {
function f11(x: NumericTag) {
>f11 : (x: 0 | 1 | 2) => "a" | "b" | "c"
>x : 0 | 1 | 2

Expand All @@ -308,7 +308,7 @@ function f11(x: Tag) {
>x : never
}

function f12(x: Tag) {
function f12(x: NumericTag) {
>f12 : (x: 0 | 1 | 2) => void
>x : 0 | 1 | 2

Expand All @@ -324,7 +324,7 @@ function f12(x: Tag) {
}
}

function f13(x: Tag) {
function f13(x: NumericTag) {
>f13 : (x: 0 | 1 | 2) => void
>x : 0 | 1 | 2

Expand Down
10 changes: 5 additions & 5 deletions tests/baselines/reference/numericLiteralTypes2.js
Expand Up @@ -66,17 +66,17 @@ function assertNever(x: never): never {
throw new Error("Unexpected value");
}

type Tag = 0 | 1 | 2;
type NumericTag = 0 | 1 | 2;

function f10(x: Tag) {
function f10(x: NumericTag) {
switch (x) {
case 0: return "a";
case 1: return "b";
case 2: return "c";
}
}

function f11(x: Tag) {
function f11(x: NumericTag) {
switch (x) {
case 0: return "a";
case 1: return "b";
Expand All @@ -85,7 +85,7 @@ function f11(x: Tag) {
return assertNever(x);
}

function f12(x: Tag) {
function f12(x: NumericTag) {
if (x) {
x;
}
Expand All @@ -94,7 +94,7 @@ function f12(x: Tag) {
}
}

function f13(x: Tag) {
function f13(x: NumericTag) {
if (x === 0 || x === 2) {
x;
}
Expand Down
22 changes: 11 additions & 11 deletions tests/baselines/reference/numericLiteralTypes2.symbols
Expand Up @@ -221,13 +221,13 @@ function assertNever(x: never): never {
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
}

type Tag = 0 | 1 | 2;
>Tag : Symbol(Tag, Decl(numericLiteralTypes2.ts, 65, 1))
type NumericTag = 0 | 1 | 2;
>NumericTag : Symbol(NumericTag, Decl(numericLiteralTypes2.ts, 65, 1))

function f10(x: Tag) {
>f10 : Symbol(f10, Decl(numericLiteralTypes2.ts, 67, 21))
function f10(x: NumericTag) {
>f10 : Symbol(f10, Decl(numericLiteralTypes2.ts, 67, 28))
>x : Symbol(x, Decl(numericLiteralTypes2.ts, 69, 13))
>Tag : Symbol(Tag, Decl(numericLiteralTypes2.ts, 65, 1))
>NumericTag : Symbol(NumericTag, Decl(numericLiteralTypes2.ts, 65, 1))

switch (x) {
>x : Symbol(x, Decl(numericLiteralTypes2.ts, 69, 13))
Expand All @@ -238,10 +238,10 @@ function f10(x: Tag) {
}
}

function f11(x: Tag) {
function f11(x: NumericTag) {
>f11 : Symbol(f11, Decl(numericLiteralTypes2.ts, 75, 1))
>x : Symbol(x, Decl(numericLiteralTypes2.ts, 77, 13))
>Tag : Symbol(Tag, Decl(numericLiteralTypes2.ts, 65, 1))
>NumericTag : Symbol(NumericTag, Decl(numericLiteralTypes2.ts, 65, 1))

switch (x) {
>x : Symbol(x, Decl(numericLiteralTypes2.ts, 77, 13))
Expand All @@ -255,10 +255,10 @@ function f11(x: Tag) {
>x : Symbol(x, Decl(numericLiteralTypes2.ts, 77, 13))
}

function f12(x: Tag) {
function f12(x: NumericTag) {
>f12 : Symbol(f12, Decl(numericLiteralTypes2.ts, 84, 1))
>x : Symbol(x, Decl(numericLiteralTypes2.ts, 86, 13))
>Tag : Symbol(Tag, Decl(numericLiteralTypes2.ts, 65, 1))
>NumericTag : Symbol(NumericTag, Decl(numericLiteralTypes2.ts, 65, 1))

if (x) {
>x : Symbol(x, Decl(numericLiteralTypes2.ts, 86, 13))
Expand All @@ -272,10 +272,10 @@ function f12(x: Tag) {
}
}

function f13(x: Tag) {
function f13(x: NumericTag) {
>f13 : Symbol(f13, Decl(numericLiteralTypes2.ts, 93, 1))
>x : Symbol(x, Decl(numericLiteralTypes2.ts, 95, 13))
>Tag : Symbol(Tag, Decl(numericLiteralTypes2.ts, 65, 1))
>NumericTag : Symbol(NumericTag, Decl(numericLiteralTypes2.ts, 65, 1))

if (x === 0 || x === 2) {
>x : Symbol(x, Decl(numericLiteralTypes2.ts, 95, 13))
Expand Down
12 changes: 6 additions & 6 deletions tests/baselines/reference/numericLiteralTypes2.types
Expand Up @@ -259,10 +259,10 @@ function assertNever(x: never): never {
>"Unexpected value" : "Unexpected value"
}

type Tag = 0 | 1 | 2;
>Tag : 0 | 1 | 2
type NumericTag = 0 | 1 | 2;
>NumericTag : 0 | 1 | 2

function f10(x: Tag) {
function f10(x: NumericTag) {
>f10 : (x: 0 | 1 | 2) => "a" | "b" | "c"
>x : 0 | 1 | 2

Expand All @@ -283,7 +283,7 @@ function f10(x: Tag) {
}
}

function f11(x: Tag) {
function f11(x: NumericTag) {
>f11 : (x: 0 | 1 | 2) => "a" | "b" | "c"
>x : 0 | 1 | 2

Expand All @@ -308,7 +308,7 @@ function f11(x: Tag) {
>x : never
}

function f12(x: Tag) {
function f12(x: NumericTag) {
>f12 : (x: 0 | 1 | 2) => void
>x : 0 | 1 | 2

Expand All @@ -324,7 +324,7 @@ function f12(x: Tag) {
}
}

function f13(x: Tag) {
function f13(x: NumericTag) {
>f13 : (x: 0 | 1 | 2) => void
>x : 0 | 1 | 2

Expand Down

0 comments on commit b38d95f

Please sign in to comment.