Skip to content

Commit 1ca5a2d

Browse files
authored
Port late bound class property declaration emit (#1728)
1 parent 869519d commit 1ca5a2d

25 files changed

+162
-219
lines changed

internal/ast/ast.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,48 @@ func (n *Node) Elements() []*Node {
10671067
return nil
10681068
}
10691069

1070+
func (n *Node) postfixToken() *Node {
1071+
switch n.Kind {
1072+
case KindEnumMember:
1073+
return n.AsEnumMember().PostfixToken
1074+
case KindPropertyAssignment:
1075+
return n.AsPropertyAssignment().PostfixToken
1076+
case KindShorthandPropertyAssignment:
1077+
return n.AsShorthandPropertyAssignment().PostfixToken
1078+
case KindPropertySignature:
1079+
return n.AsPropertySignatureDeclaration().PostfixToken
1080+
case KindPropertyDeclaration:
1081+
return n.AsPropertyDeclaration().PostfixToken
1082+
case KindMethodSignature:
1083+
return n.AsMethodSignatureDeclaration().PostfixToken
1084+
case KindMethodDeclaration:
1085+
return n.AsMethodDeclaration().PostfixToken
1086+
case KindGetAccessor:
1087+
return n.AsGetAccessorDeclaration().PostfixToken
1088+
case KindSetAccessor:
1089+
return n.AsSetAccessorDeclaration().PostfixToken
1090+
}
1091+
return nil
1092+
}
1093+
1094+
func (n *Node) QuestionToken() *TokenNode {
1095+
switch n.Kind {
1096+
case KindParameter:
1097+
return n.AsParameterDeclaration().QuestionToken
1098+
case KindConditionalExpression:
1099+
return n.AsConditionalExpression().QuestionToken
1100+
case KindMappedType:
1101+
return n.AsMappedTypeNode().QuestionToken
1102+
case KindNamedTupleMember:
1103+
return n.AsNamedTupleMember().QuestionToken
1104+
}
1105+
postfix := n.postfixToken()
1106+
if postfix != nil && postfix.Kind == KindQuestionToken {
1107+
return postfix
1108+
}
1109+
return nil
1110+
}
1111+
10701112
func (n *Node) QuestionDotToken() *Node {
10711113
switch n.Kind {
10721114
case KindElementAccessExpression:

internal/checker/checker.go

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12907,7 +12907,7 @@ func (c *Checker) getSpreadType(left *Type, right *Type, symbol *ast.Symbol, obj
1290712907

1290812908
func (c *Checker) getIndexInfoWithReadonly(info *IndexInfo, readonly bool) *IndexInfo {
1290912909
if info.isReadonly != readonly {
12910-
return c.newIndexInfo(info.keyType, info.valueType, readonly, info.declaration)
12910+
return c.newIndexInfo(info.keyType, info.valueType, readonly, info.declaration, info.components)
1291112911
}
1291212912
return info
1291312913
}
@@ -12928,7 +12928,7 @@ func (c *Checker) getUnionIndexInfos(types []*Type) []*IndexInfo {
1292812928
return c.getIndexTypeOfType(t, indexType)
1292912929
}))
1293012930
isReadonly := core.Some(types, func(t *Type) bool { return c.getIndexInfoOfType(t, indexType).isReadonly })
12931-
result = append(result, c.newIndexInfo(indexType, valueType, isReadonly, nil))
12931+
result = append(result, c.newIndexInfo(indexType, valueType, isReadonly, nil, nil))
1293212932
}
1293312933
}
1293412934
return result
@@ -17179,7 +17179,7 @@ func (c *Checker) getTypeFromObjectBindingPattern(pattern *ast.Node, includePatt
1717917179
for _, e := range pattern.AsBindingPattern().Elements.Nodes {
1718017180
name := e.PropertyNameOrName()
1718117181
if hasDotDotDotToken(e) {
17182-
stringIndexInfo = c.newIndexInfo(c.stringType, c.anyType, false /*isReadonly*/, nil)
17182+
stringIndexInfo = c.newIndexInfo(c.stringType, c.anyType, false /*isReadonly*/, nil, nil)
1718317183
continue
1718417184
}
1718517185
exprType := c.getLiteralTypeFromPropertyName(name)
@@ -17580,7 +17580,7 @@ func (c *Checker) getWidenedTypeOfObjectLiteral(t *Type, context *WideningContex
1758017580
}
1758117581
}
1758217582
result := c.newAnonymousType(t.symbol, members, nil, nil, core.SameMap(c.getIndexInfosOfType(t), func(info *IndexInfo) *IndexInfo {
17583-
return c.newIndexInfo(info.keyType, c.getWidenedType(info.valueType), info.isReadonly, info.declaration)
17583+
return c.newIndexInfo(info.keyType, c.getWidenedType(info.valueType), info.isReadonly, info.declaration, info.components)
1758417584
}))
1758517585
// Retain js literal flag through widening
1758617586
result.objectFlags |= t.objectFlags & (ObjectFlagsJSLiteral | ObjectFlagsNonInferrableType)
@@ -18187,7 +18187,7 @@ func (c *Checker) findApplicableIndexInfo(indexInfos []*IndexInfo, keyType *Type
1818718187
isReadonly = false
1818818188
}
1818918189
}
18190-
return c.newIndexInfo(c.unknownType, c.getIntersectionType(types), isReadonly, nil)
18190+
return c.newIndexInfo(c.unknownType, c.getIntersectionType(types), isReadonly, nil, nil)
1819118191
}
1819218192
}
1819318193

@@ -18794,7 +18794,7 @@ func (c *Checker) getIndexInfosOfIndexSymbol(indexSymbol *ast.Symbol, siblingSym
1879418794
}
1879518795
forEachType(c.getTypeFromTypeNode(typeNode), func(keyType *Type) {
1879618796
if c.isValidIndexKeyType(keyType) && findIndexInfo(indexInfos, keyType) == nil {
18797-
indexInfo := c.newIndexInfo(keyType, valueType, HasModifier(declaration, ast.ModifierFlagsReadonly), declaration)
18797+
indexInfo := c.newIndexInfo(keyType, valueType, HasModifier(declaration, ast.ModifierFlagsReadonly), declaration, nil)
1879818798
indexInfos = append(indexInfos, indexInfo)
1879918799
}
1880018800
})
@@ -18861,18 +18861,22 @@ func (c *Checker) getIndexInfosOfIndexSymbol(indexSymbol *ast.Symbol, siblingSym
1886118861
// NOTE: currently does not make pattern literal indexers, eg `${number}px`
1886218862
func (c *Checker) getObjectLiteralIndexInfo(isReadonly bool, properties []*ast.Symbol, keyType *Type) *IndexInfo {
1886318863
var propTypes []*Type
18864+
var components []*ast.Node
1886418865
for _, prop := range properties {
1886518866
if keyType == c.stringType && !c.isSymbolWithSymbolName(prop) ||
1886618867
keyType == c.numberType && c.isSymbolWithNumericName(prop) ||
1886718868
keyType == c.esSymbolType && c.isSymbolWithSymbolName(prop) {
1886818869
propTypes = append(propTypes, c.getTypeOfSymbol(prop))
18870+
if c.isSymbolWithComputedName(prop) {
18871+
components = append(components, prop.Declarations[0])
18872+
}
1886918873
}
1887018874
}
1887118875
unionType := c.undefinedType
1887218876
if len(propTypes) != 0 {
1887318877
unionType = c.getUnionTypeEx(propTypes, UnionReductionSubtype, nil, nil)
1887418878
}
18875-
return c.newIndexInfo(keyType, unionType, isReadonly, nil /*declaration*/)
18879+
return c.newIndexInfo(keyType, unionType, isReadonly, nil /*declaration*/, components)
1887618880
}
1887718881

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

18904+
func (c *Checker) isSymbolWithComputedName(symbol *ast.Symbol) bool {
18905+
if len(symbol.Declarations) != 0 {
18906+
name := symbol.Declarations[0].Name()
18907+
return name != nil && ast.IsComputedPropertyName(name)
18908+
}
18909+
return false
18910+
}
18911+
1890018912
func (c *Checker) isNumericName(name *ast.Node) bool {
1890118913
switch name.Kind {
1890218914
case ast.KindComputedPropertyName:
@@ -19768,7 +19780,7 @@ func (c *Checker) instantiateIndexInfo(info *IndexInfo, m *TypeMapper) *IndexInf
1976819780
if newValueType == info.valueType {
1976919781
return info
1977019782
}
19771-
return c.newIndexInfo(info.keyType, newValueType, info.isReadonly, info.declaration)
19783+
return c.newIndexInfo(info.keyType, newValueType, info.isReadonly, info.declaration, info.components)
1977219784
}
1977319785

1977419786
func (c *Checker) resolveAnonymousTypeMembers(t *Type) {
@@ -20086,7 +20098,7 @@ func (c *Checker) resolveMappedTypeMembers(t *Type) {
2008620098
propType := c.instantiateType(templateType, appendTypeMapping(t.AsMappedType().mapper, typeParameter, keyType))
2008720099
modifiersIndexInfo := c.getApplicableIndexInfo(modifiersType, propNameType)
2008820100
isReadonly := templateModifiers&MappedTypeModifiersIncludeReadonly != 0 || templateModifiers&MappedTypeModifiersExcludeReadonly == 0 && modifiersIndexInfo != nil && modifiersIndexInfo.isReadonly
20089-
indexInfo := c.newIndexInfo(indexKeyType, propType, isReadonly, nil)
20101+
indexInfo := c.newIndexInfo(indexKeyType, propType, isReadonly, nil, nil)
2009020102
indexInfos = c.appendIndexInfo(indexInfos, indexInfo, true /*union*/)
2009120103
}
2009220104
}
@@ -20479,7 +20491,7 @@ func (c *Checker) appendIndexInfo(indexInfos []*IndexInfo, newInfo *IndexInfo, u
2047920491
valueType = c.getIntersectionType([]*Type{info.valueType, newInfo.valueType})
2048020492
isReadonly = info.isReadonly && newInfo.isReadonly
2048120493
}
20482-
indexInfos[i] = c.newIndexInfo(info.keyType, valueType, isReadonly, nil)
20494+
indexInfos[i] = c.newIndexInfo(info.keyType, valueType, isReadonly, nil, nil)
2048320495
return indexInfos
2048420496
}
2048520497
}
@@ -24316,12 +24328,13 @@ func (c *Checker) newSignature(flags SignatureFlags, declaration *ast.Node, type
2431624328
return sig
2431724329
}
2431824330

24319-
func (c *Checker) newIndexInfo(keyType *Type, valueType *Type, isReadonly bool, declaration *ast.Node) *IndexInfo {
24331+
func (c *Checker) newIndexInfo(keyType *Type, valueType *Type, isReadonly bool, declaration *ast.Node, components []*ast.Node) *IndexInfo {
2432024332
info := c.indexInfoPool.New()
2432124333
info.keyType = keyType
2432224334
info.valueType = valueType
2432324335
info.isReadonly = isReadonly
2432424336
info.declaration = declaration
24337+
info.components = components
2432524338
return info
2432624339
}
2432724340

internal/checker/emitresolver.go

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,9 +1010,44 @@ func (r *emitResolver) CreateLateBoundIndexSignatures(emitContext *printer.EmitC
10101010
if info == r.checker.anyBaseTypeIndexInfo {
10111011
continue // inherited, but looks like a late-bound signature because it has no declarations
10121012
}
1013-
// if info.components {
1014-
// !!! TODO: Complete late-bound index info support - getObjectLiteralIndexInfo does not yet add late bound components to index signatures
1015-
// }
1013+
if len(info.components) != 0 {
1014+
// !!! TODO: Complete late-bound index info support - getObjectLiteralIndexInfo does not yet add late bound components to index signatures
1015+
allComponentComputedNamesSerializable := enclosingDeclaration != nil && core.Every(info.components, func(c *ast.Node) bool {
1016+
return c.Name() != nil &&
1017+
ast.IsComputedPropertyName(c.Name()) &&
1018+
ast.IsEntityNameExpression(c.Name().AsComputedPropertyName().Expression) &&
1019+
r.isEntityNameVisible(c.Name().AsComputedPropertyName().Expression, enclosingDeclaration, false).Accessibility == printer.SymbolAccessibilityAccessible
1020+
})
1021+
if allComponentComputedNamesSerializable {
1022+
for _, c := range info.components {
1023+
if r.checker.hasLateBindableName(c) {
1024+
// skip late bound props that contribute to the index signature - they'll be preserved via other means
1025+
continue
1026+
}
1027+
1028+
firstIdentifier := ast.GetFirstIdentifier(c.Name().Expression())
1029+
name := r.checker.resolveName(firstIdentifier, firstIdentifier.Text(), ast.SymbolFlagsValue|ast.SymbolFlagsExportValue, nil /*nameNotFoundMessage*/, true /*isUse*/, false /*excludeGlobals*/)
1030+
if name != nil {
1031+
tracker.TrackSymbol(name, enclosingDeclaration, ast.SymbolFlagsValue)
1032+
}
1033+
1034+
mods := core.IfElse(isStatic, []*ast.Node{emitContext.Factory.NewModifier(ast.KindStaticKeyword)}, nil)
1035+
if info.isReadonly {
1036+
mods = append(mods, emitContext.Factory.NewModifier(ast.KindReadonlyKeyword))
1037+
}
1038+
1039+
decl := emitContext.Factory.NewPropertyDeclaration(
1040+
core.IfElse(mods != nil, emitContext.Factory.NewModifierList(mods), nil),
1041+
c.Name(),
1042+
c.QuestionToken(),
1043+
requestNodeBuilder.TypeToTypeNode(r.checker.getTypeOfSymbol(c.Symbol()), enclosingDeclaration, flags, internalFlags, tracker),
1044+
nil,
1045+
)
1046+
result = append(result, decl)
1047+
}
1048+
continue
1049+
}
1050+
}
10161051
node := requestNodeBuilder.IndexInfoToIndexSignatureDeclaration(info, enclosingDeclaration, flags, internalFlags, tracker)
10171052
if node != nil && isStatic {
10181053
modNodes := []*ast.Node{emitContext.Factory.NewModifier(ast.KindStaticKeyword)}

internal/checker/inference.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,7 @@ func (c *Checker) resolveReverseMappedTypeMembers(t *Type) {
10351035
optionalMask := core.IfElse(modifiers&MappedTypeModifiersIncludeOptional != 0, 0, ast.SymbolFlagsOptional)
10361036
var indexInfos []*IndexInfo
10371037
if indexInfo != nil {
1038-
indexInfos = []*IndexInfo{c.newIndexInfo(c.stringType, core.OrElse(c.inferReverseMappedType(indexInfo.valueType, r.mappedType, r.constraintType), c.unknownType), readonlyMask && indexInfo.isReadonly, nil)}
1038+
indexInfos = []*IndexInfo{c.newIndexInfo(c.stringType, core.OrElse(c.inferReverseMappedType(indexInfo.valueType, r.mappedType, r.constraintType), c.unknownType), readonlyMask && indexInfo.isReadonly, nil, nil)}
10391039
}
10401040
members := make(ast.SymbolTable)
10411041
limitedConstraint := c.getLimitedConstraint(t)
@@ -1174,7 +1174,7 @@ func (c *Checker) createEmptyObjectTypeFromStringLiteral(t *Type) *Type {
11741174
}
11751175
var indexInfos []*IndexInfo
11761176
if t.flags&TypeFlagsString != 0 {
1177-
indexInfos = []*IndexInfo{c.newIndexInfo(c.stringType, c.emptyObjectType, false /*isReadonly*/, nil)}
1177+
indexInfos = []*IndexInfo{c.newIndexInfo(c.stringType, c.emptyObjectType, false /*isReadonly*/, nil, nil)}
11781178
}
11791179
return c.newAnonymousType(nil, members, nil, nil, indexInfos)
11801180
}

internal/checker/types.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1198,7 +1198,8 @@ type IndexInfo struct {
11981198
keyType *Type
11991199
valueType *Type
12001200
isReadonly bool
1201-
declaration *ast.Node // IndexSignatureDeclaration
1201+
declaration *ast.Node // IndexSignatureDeclaration
1202+
components []*ast.Node // ElementWithComputedPropertyName
12021203
}
12031204

12041205
/**

testdata/baselines/reference/submodule/compiler/classNonUniqueSymbolMethodHasSymbolIndexer.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ export const Mixer = Mix(class {
2727

2828

2929
//// [classNonUniqueSymbolMethodHasSymbolIndexer.d.ts]
30+
declare const a: symbol;
3031
export declare class A {
31-
[x: symbol]: () => number;
32+
[a]: () => number;
3233
}
3334
export declare const Mixer: {
3435
new (): {
@@ -37,3 +38,4 @@ export declare const Mixer: {
3738
} & (new (...args: any[]) => {
3839
mixed: true;
3940
});
41+
export {};
Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,11 @@
11
--- old.classNonUniqueSymbolMethodHasSymbolIndexer.js
22
+++ new.classNonUniqueSymbolMethodHasSymbolIndexer.js
3-
@@= skipped -26, +26 lines =@@
4-
5-
6-
//// [classNonUniqueSymbolMethodHasSymbolIndexer.d.ts]
7-
-declare const a: symbol;
8-
export declare class A {
9-
- [a]: () => number;
10-
+ [x: symbol]: () => number;
3+
@@= skipped -32, +32 lines =@@
114
}
125
export declare const Mixer: {
136
new (): {
147
- [a]: () => number;
158
+ [x: symbol]: () => number;
169
};
1710
} & (new (...args: any[]) => {
18-
mixed: true;
19-
});
20-
-export {};
11+
mixed: true;

testdata/baselines/reference/submodule/compiler/declarationEmitAnyComputedPropertyInClass.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ exports.C = C;
2424

2525

2626
//// [main.d.ts]
27+
import Test from "abcdefgh";
2728
export declare class C {
28-
[x: number]: () => void;
29+
[Test.someKey]: () => void;
2930
}

testdata/baselines/reference/submodule/compiler/declarationEmitAnyComputedPropertyInClass.js.diff

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,4 @@
88
+const abcdefgh_1 = require("abcdefgh");
99
class C {
1010
[abcdefgh_1.default.someKey]() { }
11-
;
12-
@@= skipped -9, +9 lines =@@
13-
14-
15-
//// [main.d.ts]
16-
-import Test from "abcdefgh";
17-
export declare class C {
18-
- [Test.someKey]: () => void;
19-
+ [x: number]: () => void;
20-
}
11+
;

testdata/baselines/reference/submodule/compiler/declarationEmitComputedNameWithQuestionToken.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,6 @@ exports.a = (new WithData())["ahahahaahah"]();
2929
//// [declarationEmitComputedNameWithQuestionToken.d.ts]
3030
export declare const dataSomething: `data-${string}`;
3131
export declare class WithData {
32-
[x: string]: () => string;
32+
[dataSomething]?: () => string;
3333
}
3434
export declare const a: string;

0 commit comments

Comments
 (0)