Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,48 @@ func (n *Node) Elements() []*Node {
return nil
}

func (n *Node) postfixToken() *Node {
switch n.Kind {
case KindEnumMember:
return n.AsEnumMember().PostfixToken
case KindPropertyAssignment:
return n.AsPropertyAssignment().PostfixToken
case KindShorthandPropertyAssignment:
return n.AsShorthandPropertyAssignment().PostfixToken
case KindPropertySignature:
return n.AsPropertySignatureDeclaration().PostfixToken
case KindPropertyDeclaration:
return n.AsPropertyDeclaration().PostfixToken
case KindMethodSignature:
return n.AsMethodSignatureDeclaration().PostfixToken
case KindMethodDeclaration:
return n.AsMethodDeclaration().PostfixToken
case KindGetAccessor:
return n.AsGetAccessorDeclaration().PostfixToken
case KindSetAccessor:
return n.AsSetAccessorDeclaration().PostfixToken
}
return nil
}

func (n *Node) QuestionToken() *TokenNode {
switch n.Kind {
case KindParameter:
return n.AsParameterDeclaration().QuestionToken
case KindConditionalExpression:
return n.AsConditionalExpression().QuestionToken
case KindMappedType:
return n.AsMappedTypeNode().QuestionToken
case KindNamedTupleMember:
return n.AsNamedTupleMember().QuestionToken
}
postfix := n.postfixToken()
if postfix != nil && postfix.Kind == KindQuestionToken {
return postfix
}
return nil
}

func (n *Node) QuestionDotToken() *Node {
switch n.Kind {
case KindElementAccessExpression:
Expand Down
35 changes: 24 additions & 11 deletions internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -12907,7 +12907,7 @@ func (c *Checker) getSpreadType(left *Type, right *Type, symbol *ast.Symbol, obj

func (c *Checker) getIndexInfoWithReadonly(info *IndexInfo, readonly bool) *IndexInfo {
if info.isReadonly != readonly {
return c.newIndexInfo(info.keyType, info.valueType, readonly, info.declaration)
return c.newIndexInfo(info.keyType, info.valueType, readonly, info.declaration, info.components)
}
return info
}
Expand All @@ -12928,7 +12928,7 @@ func (c *Checker) getUnionIndexInfos(types []*Type) []*IndexInfo {
return c.getIndexTypeOfType(t, indexType)
}))
isReadonly := core.Some(types, func(t *Type) bool { return c.getIndexInfoOfType(t, indexType).isReadonly })
result = append(result, c.newIndexInfo(indexType, valueType, isReadonly, nil))
result = append(result, c.newIndexInfo(indexType, valueType, isReadonly, nil, nil))
}
}
return result
Expand Down Expand Up @@ -17179,7 +17179,7 @@ func (c *Checker) getTypeFromObjectBindingPattern(pattern *ast.Node, includePatt
for _, e := range pattern.AsBindingPattern().Elements.Nodes {
name := e.PropertyNameOrName()
if hasDotDotDotToken(e) {
stringIndexInfo = c.newIndexInfo(c.stringType, c.anyType, false /*isReadonly*/, nil)
stringIndexInfo = c.newIndexInfo(c.stringType, c.anyType, false /*isReadonly*/, nil, nil)
continue
}
exprType := c.getLiteralTypeFromPropertyName(name)
Expand Down Expand Up @@ -17580,7 +17580,7 @@ func (c *Checker) getWidenedTypeOfObjectLiteral(t *Type, context *WideningContex
}
}
result := c.newAnonymousType(t.symbol, members, nil, nil, core.SameMap(c.getIndexInfosOfType(t), func(info *IndexInfo) *IndexInfo {
return c.newIndexInfo(info.keyType, c.getWidenedType(info.valueType), info.isReadonly, info.declaration)
return c.newIndexInfo(info.keyType, c.getWidenedType(info.valueType), info.isReadonly, info.declaration, info.components)
}))
// Retain js literal flag through widening
result.objectFlags |= t.objectFlags & (ObjectFlagsJSLiteral | ObjectFlagsNonInferrableType)
Expand Down Expand Up @@ -18187,7 +18187,7 @@ func (c *Checker) findApplicableIndexInfo(indexInfos []*IndexInfo, keyType *Type
isReadonly = false
}
}
return c.newIndexInfo(c.unknownType, c.getIntersectionType(types), isReadonly, nil)
return c.newIndexInfo(c.unknownType, c.getIntersectionType(types), isReadonly, nil, nil)
}
}

Expand Down Expand Up @@ -18794,7 +18794,7 @@ func (c *Checker) getIndexInfosOfIndexSymbol(indexSymbol *ast.Symbol, siblingSym
}
forEachType(c.getTypeFromTypeNode(typeNode), func(keyType *Type) {
if c.isValidIndexKeyType(keyType) && findIndexInfo(indexInfos, keyType) == nil {
indexInfo := c.newIndexInfo(keyType, valueType, HasModifier(declaration, ast.ModifierFlagsReadonly), declaration)
indexInfo := c.newIndexInfo(keyType, valueType, HasModifier(declaration, ast.ModifierFlagsReadonly), declaration, nil)
indexInfos = append(indexInfos, indexInfo)
}
})
Expand Down Expand Up @@ -18861,18 +18861,22 @@ func (c *Checker) getIndexInfosOfIndexSymbol(indexSymbol *ast.Symbol, siblingSym
// NOTE: currently does not make pattern literal indexers, eg `${number}px`
func (c *Checker) getObjectLiteralIndexInfo(isReadonly bool, properties []*ast.Symbol, keyType *Type) *IndexInfo {
var propTypes []*Type
var components []*ast.Node
for _, prop := range properties {
if keyType == c.stringType && !c.isSymbolWithSymbolName(prop) ||
keyType == c.numberType && c.isSymbolWithNumericName(prop) ||
keyType == c.esSymbolType && c.isSymbolWithSymbolName(prop) {
propTypes = append(propTypes, c.getTypeOfSymbol(prop))
if c.isSymbolWithComputedName(prop) {
components = append(components, prop.Declarations[0])
}
}
}
unionType := c.undefinedType
if len(propTypes) != 0 {
unionType = c.getUnionTypeEx(propTypes, UnionReductionSubtype, nil, nil)
}
return c.newIndexInfo(keyType, unionType, isReadonly, nil /*declaration*/)
return c.newIndexInfo(keyType, unionType, isReadonly, nil /*declaration*/, components)
}

func (c *Checker) isSymbolWithSymbolName(symbol *ast.Symbol) bool {
Expand All @@ -18897,6 +18901,14 @@ func (c *Checker) isSymbolWithNumericName(symbol *ast.Symbol) bool {
return false
}

func (c *Checker) isSymbolWithComputedName(symbol *ast.Symbol) bool {
if len(symbol.Declarations) != 0 {
name := symbol.Declarations[0].Name()
return name != nil && ast.IsComputedPropertyName(name)
}
return false
}

func (c *Checker) isNumericName(name *ast.Node) bool {
switch name.Kind {
case ast.KindComputedPropertyName:
Expand Down Expand Up @@ -19768,7 +19780,7 @@ func (c *Checker) instantiateIndexInfo(info *IndexInfo, m *TypeMapper) *IndexInf
if newValueType == info.valueType {
return info
}
return c.newIndexInfo(info.keyType, newValueType, info.isReadonly, info.declaration)
return c.newIndexInfo(info.keyType, newValueType, info.isReadonly, info.declaration, info.components)
}

func (c *Checker) resolveAnonymousTypeMembers(t *Type) {
Expand Down Expand Up @@ -20086,7 +20098,7 @@ func (c *Checker) resolveMappedTypeMembers(t *Type) {
propType := c.instantiateType(templateType, appendTypeMapping(t.AsMappedType().mapper, typeParameter, keyType))
modifiersIndexInfo := c.getApplicableIndexInfo(modifiersType, propNameType)
isReadonly := templateModifiers&MappedTypeModifiersIncludeReadonly != 0 || templateModifiers&MappedTypeModifiersExcludeReadonly == 0 && modifiersIndexInfo != nil && modifiersIndexInfo.isReadonly
indexInfo := c.newIndexInfo(indexKeyType, propType, isReadonly, nil)
indexInfo := c.newIndexInfo(indexKeyType, propType, isReadonly, nil, nil)
indexInfos = c.appendIndexInfo(indexInfos, indexInfo, true /*union*/)
}
}
Expand Down Expand Up @@ -20479,7 +20491,7 @@ func (c *Checker) appendIndexInfo(indexInfos []*IndexInfo, newInfo *IndexInfo, u
valueType = c.getIntersectionType([]*Type{info.valueType, newInfo.valueType})
isReadonly = info.isReadonly && newInfo.isReadonly
}
indexInfos[i] = c.newIndexInfo(info.keyType, valueType, isReadonly, nil)
indexInfos[i] = c.newIndexInfo(info.keyType, valueType, isReadonly, nil, nil)
return indexInfos
}
}
Expand Down Expand Up @@ -24316,12 +24328,13 @@ func (c *Checker) newSignature(flags SignatureFlags, declaration *ast.Node, type
return sig
}

func (c *Checker) newIndexInfo(keyType *Type, valueType *Type, isReadonly bool, declaration *ast.Node) *IndexInfo {
func (c *Checker) newIndexInfo(keyType *Type, valueType *Type, isReadonly bool, declaration *ast.Node, components []*ast.Node) *IndexInfo {
info := c.indexInfoPool.New()
info.keyType = keyType
info.valueType = valueType
info.isReadonly = isReadonly
info.declaration = declaration
info.components = components
return info
}

Expand Down
41 changes: 38 additions & 3 deletions internal/checker/emitresolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1010,9 +1010,44 @@ func (r *emitResolver) CreateLateBoundIndexSignatures(emitContext *printer.EmitC
if info == r.checker.anyBaseTypeIndexInfo {
continue // inherited, but looks like a late-bound signature because it has no declarations
}
// if info.components {
// !!! TODO: Complete late-bound index info support - getObjectLiteralIndexInfo does not yet add late bound components to index signatures
// }
if len(info.components) != 0 {
// !!! TODO: Complete late-bound index info support - getObjectLiteralIndexInfo does not yet add late bound components to index signatures
allComponentComputedNamesSerializable := enclosingDeclaration != nil && core.Every(info.components, func(c *ast.Node) bool {
return c.Name() != nil &&
ast.IsComputedPropertyName(c.Name()) &&
ast.IsEntityNameExpression(c.Name().AsComputedPropertyName().Expression) &&
r.isEntityNameVisible(c.Name().AsComputedPropertyName().Expression, enclosingDeclaration, false).Accessibility == printer.SymbolAccessibilityAccessible
})
if allComponentComputedNamesSerializable {
for _, c := range info.components {
if r.checker.hasLateBindableName(c) {
// skip late bound props that contribute to the index signature - they'll be preserved via other means
continue
}

firstIdentifier := ast.GetFirstIdentifier(c.Name().Expression())
name := r.checker.resolveName(firstIdentifier, firstIdentifier.Text(), ast.SymbolFlagsValue|ast.SymbolFlagsExportValue, nil /*nameNotFoundMessage*/, true /*isUse*/, false /*excludeGlobals*/)
if name != nil {
tracker.TrackSymbol(name, enclosingDeclaration, ast.SymbolFlagsValue)
}

mods := core.IfElse(isStatic, []*ast.Node{emitContext.Factory.NewModifier(ast.KindStaticKeyword)}, nil)
if info.isReadonly {
mods = append(mods, emitContext.Factory.NewModifier(ast.KindReadonlyKeyword))
}

decl := emitContext.Factory.NewPropertyDeclaration(
core.IfElse(mods != nil, emitContext.Factory.NewModifierList(mods), nil),
c.Name(),
c.QuestionToken(),
requestNodeBuilder.TypeToTypeNode(r.checker.getTypeOfSymbol(c.Symbol()), enclosingDeclaration, flags, internalFlags, tracker),
nil,
)
result = append(result, decl)
}
continue
}
}
node := requestNodeBuilder.IndexInfoToIndexSignatureDeclaration(info, enclosingDeclaration, flags, internalFlags, tracker)
if node != nil && isStatic {
modNodes := []*ast.Node{emitContext.Factory.NewModifier(ast.KindStaticKeyword)}
Expand Down
4 changes: 2 additions & 2 deletions internal/checker/inference.go
Original file line number Diff line number Diff line change
Expand Up @@ -1035,7 +1035,7 @@ func (c *Checker) resolveReverseMappedTypeMembers(t *Type) {
optionalMask := core.IfElse(modifiers&MappedTypeModifiersIncludeOptional != 0, 0, ast.SymbolFlagsOptional)
var indexInfos []*IndexInfo
if indexInfo != nil {
indexInfos = []*IndexInfo{c.newIndexInfo(c.stringType, core.OrElse(c.inferReverseMappedType(indexInfo.valueType, r.mappedType, r.constraintType), c.unknownType), readonlyMask && indexInfo.isReadonly, nil)}
indexInfos = []*IndexInfo{c.newIndexInfo(c.stringType, core.OrElse(c.inferReverseMappedType(indexInfo.valueType, r.mappedType, r.constraintType), c.unknownType), readonlyMask && indexInfo.isReadonly, nil, nil)}
}
members := make(ast.SymbolTable)
limitedConstraint := c.getLimitedConstraint(t)
Expand Down Expand Up @@ -1174,7 +1174,7 @@ func (c *Checker) createEmptyObjectTypeFromStringLiteral(t *Type) *Type {
}
var indexInfos []*IndexInfo
if t.flags&TypeFlagsString != 0 {
indexInfos = []*IndexInfo{c.newIndexInfo(c.stringType, c.emptyObjectType, false /*isReadonly*/, nil)}
indexInfos = []*IndexInfo{c.newIndexInfo(c.stringType, c.emptyObjectType, false /*isReadonly*/, nil, nil)}
}
return c.newAnonymousType(nil, members, nil, nil, indexInfos)
}
Expand Down
3 changes: 2 additions & 1 deletion internal/checker/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1198,7 +1198,8 @@ type IndexInfo struct {
keyType *Type
valueType *Type
isReadonly bool
declaration *ast.Node // IndexSignatureDeclaration
declaration *ast.Node // IndexSignatureDeclaration
components []*ast.Node // ElementWithComputedPropertyName
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ export const Mixer = Mix(class {


//// [classNonUniqueSymbolMethodHasSymbolIndexer.d.ts]
declare const a: symbol;
export declare class A {
[x: symbol]: () => number;
[a]: () => number;
}
export declare const Mixer: {
new (): {
Expand All @@ -37,3 +38,4 @@ export declare const Mixer: {
} & (new (...args: any[]) => {
mixed: true;
});
export {};
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
--- old.classNonUniqueSymbolMethodHasSymbolIndexer.js
+++ new.classNonUniqueSymbolMethodHasSymbolIndexer.js
@@= skipped -26, +26 lines =@@


//// [classNonUniqueSymbolMethodHasSymbolIndexer.d.ts]
-declare const a: symbol;
export declare class A {
- [a]: () => number;
+ [x: symbol]: () => number;
@@= skipped -32, +32 lines =@@
}
export declare const Mixer: {
new (): {
- [a]: () => number;
+ [x: symbol]: () => number;
};
} & (new (...args: any[]) => {
mixed: true;
});
-export {};
mixed: true;
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ exports.C = C;


//// [main.d.ts]
import Test from "abcdefgh";
export declare class C {
[x: number]: () => void;
[Test.someKey]: () => void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,4 @@
+const abcdefgh_1 = require("abcdefgh");
class C {
[abcdefgh_1.default.someKey]() { }
;
@@= skipped -9, +9 lines =@@


//// [main.d.ts]
-import Test from "abcdefgh";
export declare class C {
- [Test.someKey]: () => void;
+ [x: number]: () => void;
}
;
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ exports.a = (new WithData())["ahahahaahah"]();
//// [declarationEmitComputedNameWithQuestionToken.d.ts]
export declare const dataSomething: `data-${string}`;
export declare class WithData {
[x: string]: () => string;
[dataSomething]?: () => string;
}
export declare const a: string;

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ exports.Test = Test;


//// [declarationEmitMultipleComputedNamesSameDomain.d.ts]
declare const x: string;
declare const y: "y";
export declare class Test {
[x: string]: number;
[x]: number;
[y]: number;
}
export {};
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,4 @@
-_a = x, _b = y;


//// [declarationEmitMultipleComputedNamesSameDomain.d.ts]
-declare const x: string;
declare const y: "y";
export declare class Test {
- [x]: number;
+ [x: string]: number;
[y]: number;
}
export {};
//// [declarationEmitMultipleComputedNamesSameDomain.d.ts]
Loading
Loading