Allow indexing generics with unique symbols (#22339)

* Allow indexing generics with unique symbols

* Move condition to assert
This commit is contained in:
Wesley Wigham 2018-03-05 13:58:19 -08:00 committed by GitHub
parent 53ae507545
commit 16bf02991d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 199 additions and 11 deletions

View file

@ -3725,8 +3725,8 @@ namespace ts {
return "(Anonymous function)";
}
}
if ((symbol as TransientSymbol).syntheticLiteralTypeOrigin) {
const stringValue = (symbol as TransientSymbol).syntheticLiteralTypeOrigin.value;
if ((symbol as TransientSymbol).nameType && (symbol as TransientSymbol).nameType.flags & TypeFlags.StringLiteral) {
const stringValue = ((symbol as TransientSymbol).nameType as StringLiteralType).value;
if (!isIdentifierText(stringValue, compilerOptions.target)) {
return `"${escapeString(stringValue, CharacterCodes.doubleQuote)}"`;
}
@ -5460,6 +5460,12 @@ namespace ts {
lateSymbol = createSymbol(SymbolFlags.None, memberName, CheckFlags.Late);
}
const symbolLinks = getSymbolLinks(lateSymbol);
if (!symbolLinks.nameType) {
// Retain link to name type so that it can be reused later
symbolLinks.nameType = type;
}
addDeclarationToLateBoundSymbol(lateSymbol, decl, symbolFlags);
if (lateSymbol.parent) {
Debug.assert(lateSymbol.parent === parent, "Existing symbol parent should match new one");
@ -5948,7 +5954,7 @@ namespace ts {
// If the current iteration type constituent is a string literal type, create a property.
// Otherwise, for type string create a string index signature.
if (t.flags & TypeFlags.StringLiteral) {
const propName = escapeLeadingUnderscores((<StringLiteralType>t).value);
const propName = getLateBoundNameFromType(t as LiteralType | UniqueESSymbolType);
const modifiersProp = getPropertyOfType(modifiersType, propName);
const isOptional = !!(templateModifiers & MappedTypeModifiers.IncludeOptional ||
!(templateModifiers & MappedTypeModifiers.ExcludeOptional) && modifiersProp && modifiersProp.flags & SymbolFlags.Optional);
@ -5965,7 +5971,7 @@ namespace ts {
prop.syntheticOrigin = propertySymbol;
prop.declarations = propertySymbol.declarations;
}
prop.syntheticLiteralTypeOrigin = t as StringLiteralType;
prop.nameType = t;
members.set(propName, prop);
}
else if (t.flags & (TypeFlags.Any | TypeFlags.String)) {
@ -7999,9 +8005,19 @@ namespace ts {
}
function getLiteralTypeFromPropertyName(prop: Symbol) {
return getDeclarationModifierFlagsFromSymbol(prop) & ModifierFlags.NonPublicAccessibilityModifier || isKnownSymbol(prop) ?
neverType :
getLiteralType(symbolName(prop));
const links = getSymbolLinks(prop);
if (!links.nameType) {
if (links.target) {
Debug.assert(links.target.escapedName === prop.escapedName, "Target symbol and symbol do not have the same name");
links.nameType = getLiteralTypeFromPropertyName(links.target);
}
else {
links.nameType = getDeclarationModifierFlagsFromSymbol(prop) & ModifierFlags.NonPublicAccessibilityModifier || isKnownSymbol(prop) ?
neverType :
getLiteralType(symbolName(prop));
}
}
return links.nameType;
}
function getLiteralTypeFromPropertyNames(type: Type) {
@ -11222,7 +11238,7 @@ namespace ts {
result.type = undefinedType;
const associatedKeyType = getLiteralType(unescapeLeadingUnderscores(name));
if (associatedKeyType.flags & TypeFlags.StringLiteral) {
result.syntheticLiteralTypeOrigin = associatedKeyType as StringLiteralType;
result.nameType = associatedKeyType;
}
undefinedProperties.set(name, result);
return result;
@ -15004,10 +15020,15 @@ namespace ts {
typeFlags |= type.flags;
const nameType = hasLateBindableName(memberDecl) ? checkComputedPropertyName(memberDecl.name) : undefined;
const prop = nameType && isTypeUsableAsLateBoundName(nameType)
? createSymbol(SymbolFlags.Property | member.flags, getLateBoundNameFromType(nameType), CheckFlags.Late)
const hasLateBoundName = nameType && isTypeUsableAsLateBoundName(nameType);
const prop = hasLateBoundName
? createSymbol(SymbolFlags.Property | member.flags, getLateBoundNameFromType(nameType as LiteralType | UniqueESSymbolType), CheckFlags.Late)
: createSymbol(SymbolFlags.Property | member.flags, literalName || member.escapedName);
if (hasLateBoundName) {
prop.nameType = nameType;
}
if (inDestructuringPattern) {
// If object literal is an assignment pattern and if the assignment pattern specifies a default value
// for the property, make the property optional.

View file

@ -3344,7 +3344,6 @@ namespace ts {
leftSpread?: Symbol; // Left source for synthetic spread property
rightSpread?: Symbol; // Right source for synthetic spread property
syntheticOrigin?: Symbol; // For a property on a mapped or spread type, points back to the original property
syntheticLiteralTypeOrigin?: StringLiteralType; // For a property on a mapped type, indicates the type whose text to use as the declaration name, instead of the symbol name
isDiscriminantProperty?: boolean; // True if discriminant synthetic property
resolvedExports?: SymbolTable; // Resolved exports of module or combined early- and late-bound static members of a class.
resolvedMembers?: SymbolTable; // Combined early- and late-bound members of a symbol
@ -3356,6 +3355,7 @@ namespace ts {
enumKind?: EnumKind; // Enum declaration classification
originatingImport?: ImportDeclaration | ImportCall; // Import declaration which produced the symbol, present if the symbol is marked as uncallable but had call signatures in `resolveESModuleSymbol`
lateSymbol?: Symbol; // Late-bound symbol for a computed property
nameType?: Type; // Type associate with a late-bound or mapped type property symbol's name
}
/* @internal */

View file

@ -0,0 +1,33 @@
//// [lateBoundConstraintTypeChecksCorrectly.ts]
declare const fooProp: unique symbol;
declare const barProp: unique symbol;
type BothProps = typeof fooProp | typeof barProp;
export interface Foo<T> {
[fooProp]: T;
[barProp]: string;
}
function f<T extends Foo<number>>(x: T) {
const abc = x[fooProp]; // expected: 'T[typeof fooProp]'
/**
* Expected: no error
*/
const def: T[typeof fooProp] = x[fooProp];
const def2: T[typeof barProp] = x[barProp];
}
//// [lateBoundConstraintTypeChecksCorrectly.js]
"use strict";
exports.__esModule = true;
function f(x) {
var abc = x[fooProp]; // expected: 'T[typeof fooProp]'
/**
* Expected: no error
*/
var def = x[fooProp];
var def2 = x[barProp];
}

View file

@ -0,0 +1,56 @@
=== tests/cases/compiler/lateBoundConstraintTypeChecksCorrectly.ts ===
declare const fooProp: unique symbol;
>fooProp : Symbol(fooProp, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 0, 13))
declare const barProp: unique symbol;
>barProp : Symbol(barProp, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 1, 13))
type BothProps = typeof fooProp | typeof barProp;
>BothProps : Symbol(BothProps, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 1, 37))
>fooProp : Symbol(fooProp, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 0, 13))
>barProp : Symbol(barProp, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 1, 13))
export interface Foo<T> {
>Foo : Symbol(Foo, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 3, 49))
>T : Symbol(T, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 5, 21))
[fooProp]: T;
>[fooProp] : Symbol(Foo[fooProp], Decl(lateBoundConstraintTypeChecksCorrectly.ts, 5, 25))
>fooProp : Symbol(fooProp, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 0, 13))
>T : Symbol(T, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 5, 21))
[barProp]: string;
>[barProp] : Symbol(Foo[barProp], Decl(lateBoundConstraintTypeChecksCorrectly.ts, 6, 15))
>barProp : Symbol(barProp, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 1, 13))
}
function f<T extends Foo<number>>(x: T) {
>f : Symbol(f, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 8, 1))
>T : Symbol(T, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 10, 11))
>Foo : Symbol(Foo, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 3, 49))
>x : Symbol(x, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 10, 34))
>T : Symbol(T, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 10, 11))
const abc = x[fooProp]; // expected: 'T[typeof fooProp]'
>abc : Symbol(abc, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 11, 9))
>x : Symbol(x, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 10, 34))
>fooProp : Symbol(fooProp, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 0, 13))
/**
* Expected: no error
*/
const def: T[typeof fooProp] = x[fooProp];
>def : Symbol(def, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 16, 9))
>T : Symbol(T, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 10, 11))
>fooProp : Symbol(fooProp, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 0, 13))
>x : Symbol(x, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 10, 34))
>fooProp : Symbol(fooProp, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 0, 13))
const def2: T[typeof barProp] = x[barProp];
>def2 : Symbol(def2, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 17, 9))
>T : Symbol(T, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 10, 11))
>barProp : Symbol(barProp, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 1, 13))
>x : Symbol(x, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 10, 34))
>barProp : Symbol(barProp, Decl(lateBoundConstraintTypeChecksCorrectly.ts, 1, 13))
}

View file

@ -0,0 +1,59 @@
=== tests/cases/compiler/lateBoundConstraintTypeChecksCorrectly.ts ===
declare const fooProp: unique symbol;
>fooProp : unique symbol
declare const barProp: unique symbol;
>barProp : unique symbol
type BothProps = typeof fooProp | typeof barProp;
>BothProps : unique symbol | unique symbol
>fooProp : unique symbol
>barProp : unique symbol
export interface Foo<T> {
>Foo : Foo<T>
>T : T
[fooProp]: T;
>[fooProp] : T
>fooProp : unique symbol
>T : T
[barProp]: string;
>[barProp] : string
>barProp : unique symbol
}
function f<T extends Foo<number>>(x: T) {
>f : <T extends Foo<number>>(x: T) => void
>T : T
>Foo : Foo<T>
>x : T
>T : T
const abc = x[fooProp]; // expected: 'T[typeof fooProp]'
>abc : number
>x[fooProp] : number
>x : T
>fooProp : unique symbol
/**
* Expected: no error
*/
const def: T[typeof fooProp] = x[fooProp];
>def : T[unique symbol]
>T : T
>fooProp : unique symbol
>x[fooProp] : number
>x : T
>fooProp : unique symbol
const def2: T[typeof barProp] = x[barProp];
>def2 : T[unique symbol]
>T : T
>barProp : unique symbol
>x[barProp] : string
>x : T
>barProp : unique symbol
}

View file

@ -0,0 +1,19 @@
declare const fooProp: unique symbol;
declare const barProp: unique symbol;
type BothProps = typeof fooProp | typeof barProp;
export interface Foo<T> {
[fooProp]: T;
[barProp]: string;
}
function f<T extends Foo<number>>(x: T) {
const abc = x[fooProp]; // expected: 'T[typeof fooProp]'
/**
* Expected: no error
*/
const def: T[typeof fooProp] = x[fooProp];
const def2: T[typeof barProp] = x[barProp];
}