Allow indexing generics with unique symbols (#22339)
* Allow indexing generics with unique symbols * Move condition to assert
This commit is contained in:
parent
53ae507545
commit
16bf02991d
6 changed files with 199 additions and 11 deletions
|
@ -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.
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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];
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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];
|
||||
}
|
Loading…
Reference in a new issue