Skip to content

Commit 93550a8

Browse files
committed
Add 'declaredCount' for easy duplicate index check.
Fix fourslash test. Add potential cases for union as an index signature.
1 parent 0dd4399 commit 93550a8

9 files changed

+290
-218
lines changed

src/compiler/checker.ts

Lines changed: 44 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@ module ts {
77
let nextMergeId = 1;
88

99
interface IndexTypeMap {
10-
[x: number]: IndexType
10+
[x: IndexKind]: IndexType
1111
}
12-
// TODO: LKG and replace [x: IndexKind]
1312

1413
// @internal
1514
export function getNodeId(node: Node): number {
@@ -1637,7 +1636,8 @@ module ts {
16371636
}
16381637

16391638
function getIndexerParameterName(type: ObjectType, indexKind: IndexKind, fallbackName: string): string {
1640-
let declaration = <SignatureDeclaration>getIndexDeclarationOfSymbol(type.symbol, indexKind);
1639+
let indexMap = getDeclaredIndexTypesOfSymbol(type.symbol);
1640+
let declaration = indexMap[indexKind] ? indexMap[indexKind].declaredNode : null;
16411641
if (!declaration) {
16421642
// declaration might not be found if indexer was added from the contextual type.
16431643
// in this case use fallback name
@@ -1696,21 +1696,21 @@ module ts {
16961696
writePunctuation(writer, SyntaxKind.SemicolonToken);
16971697
writer.writeLine();
16981698
}
1699-
function writeIndexType(indexType: IndexType, kind: IndexKind) {
1699+
function writeIndexType(indexType: IndexType) {
17001700
if (!indexType) {
17011701
return;
17021702
}
17031703
writePunctuation(writer, SyntaxKind.OpenBracketToken);
1704-
writer.writeParameter(getIndexerParameterName(resolved, kind, /*fallbackName*/"x"));
1704+
writer.writeParameter(getIndexerParameterName(resolved, indexType.kind, /*fallbackName*/"x"));
17051705
writePunctuation(writer, SyntaxKind.ColonToken);
17061706
writeSpace(writer);
17071707

1708-
if (indexType.typeOfIndex && (indexType.typeOfIndex.flags & TypeFlags.Enum)) {
1708+
if (indexType.typeOfIndex && (indexType.typeOfIndex.flags & TypeFlags.Subset)) {
17091709
writeType(indexType.typeOfIndex, TypeFormatFlags.None);
1710-
writer.writeStringLiteral(kind === IndexKind.String ? "<string>" : "<number>");
1710+
writer.writeStringLiteral(indexType.kind === IndexKind.String ? " < string" : " < number");
17111711
}
17121712
else {
1713-
writeKeyword(writer, kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword);
1713+
writeKeyword(writer, indexType.kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword);
17141714
}
17151715

17161716
writePunctuation(writer, SyntaxKind.CloseBracketToken);
@@ -1720,8 +1720,8 @@ module ts {
17201720
writePunctuation(writer, SyntaxKind.SemicolonToken);
17211721
writer.writeLine();
17221722
}
1723-
writeIndexType(resolved.stringIndex, IndexKind.String)
1724-
writeIndexType(resolved.numberIndex, IndexKind.Number)
1723+
writeIndexType(resolved.stringIndex)
1724+
writeIndexType(resolved.numberIndex)
17251725

17261726
for (let p of resolved.properties) {
17271727
let t = getTypeOfSymbol(p);
@@ -3352,25 +3352,6 @@ module ts {
33523352
return symbol.members["__index"];
33533353
}
33543354

3355-
function getIndexDeclarationOfSymbol(symbol: Symbol, kind: IndexKind): SignatureDeclaration {
3356-
let flagCheck = kind === IndexKind.Number ? TypeFlags.NumberLike : TypeFlags.String;
3357-
let indexSymbol = getIndexSymbol(symbol);
3358-
if (indexSymbol) {
3359-
let len = indexSymbol.declarations.length;
3360-
for (let decl of indexSymbol.declarations) {
3361-
let node = <SignatureDeclaration>decl;
3362-
if (node.parameters.length === 1) {
3363-
let type = getTypeFromIndexSignatureParameter(node.parameters[0]);
3364-
if(type && (type.flags & flagCheck)) {
3365-
return node;
3366-
}
3367-
}
3368-
}
3369-
}
3370-
3371-
return undefined;
3372-
}
3373-
33743355
function getDeclaredIndexTypesOfSymbol(symbol: Symbol): IndexTypeMap {
33753356
let indexMap: IndexTypeMap = []
33763357
let indexSymbol = getIndexSymbol(symbol);
@@ -3382,12 +3363,17 @@ module ts {
33823363
continue;
33833364
}
33843365
let kind = (type.flags & TypeFlags.NumberLike) ? IndexKind.Number : (type.flags & TypeFlags.String) ? IndexKind.String: null;
3385-
if(kind !== null) {
3386-
indexMap[kind] = {
3387-
kind: kind,
3388-
typeOfIndex: type,
3389-
typeOfValue: decl.type ? getTypeFromTypeNodeOrHeritageClauseElement(decl.type) : anyType,
3390-
declaredNode: decl
3366+
if (kind !== null) {
3367+
if (indexMap[kind]) {
3368+
indexMap[kind].declaredCount++;
3369+
} else {
3370+
indexMap[kind] = {
3371+
kind: kind,
3372+
typeOfIndex: type,
3373+
typeOfValue: decl.type ? getTypeFromTypeNodeOrHeritageClauseElement(decl.type) : anyType,
3374+
declaredNode: decl,
3375+
declaredCount: 1
3376+
}
33913377
}
33923378
}
33933379
}
@@ -8326,34 +8312,31 @@ module ts {
83268312
// TypeScript 1.0 spec (April 2014)
83278313
// 3.7.4: An object type can contain at most one string index signature and one numeric index signature.
83288314
// 8.5: A class declaration can have at most one string index member declaration and one numeric index member declaration
8329-
let indexSymbol = getIndexSymbol(getSymbolOfNode(node));
8330-
if (indexSymbol) {
8331-
let seenNumericIndexer = false;
8332-
let seenStringIndexer = false;
8315+
let symbol = getSymbolOfNode(node);
8316+
let indexMap = getDeclaredIndexTypesOfSymbol(symbol);
8317+
let stringIndex = indexMap[IndexKind.String];
8318+
let numberIndex = indexMap[IndexKind.Number];
8319+
8320+
if (stringIndex && stringIndex.declaredCount > 1) {
8321+
errorDuplicateIndex(getIndexSymbol(symbol), stringIndex);
8322+
}
8323+
8324+
if (numberIndex && numberIndex.declaredCount > 1) {
8325+
errorDuplicateIndex(getIndexSymbol(symbol), numberIndex);
8326+
}
8327+
8328+
function errorDuplicateIndex(indexSymbol: Symbol, indexType: IndexType) {
83338329
for (let decl of indexSymbol.declarations) {
83348330
let declaration = <SignatureDeclaration>decl;
8335-
if (declaration.parameters.length === 1) {
8336-
let type = getTypeFromIndexSignatureParameter(declaration.parameters[0])
8337-
if (!type) {
8338-
continue;
8339-
}
8340-
8341-
if (type.flags & TypeFlags.String) {
8342-
if (!seenStringIndexer) {
8343-
seenStringIndexer = true;
8344-
}
8345-
else {
8346-
error(declaration, Diagnostics.Duplicate_string_index_signature);
8347-
}
8348-
}
8349-
else if (type.flags & TypeFlags.NumberLike) {
8350-
if (!seenNumericIndexer) {
8351-
seenNumericIndexer = true;
8352-
}
8353-
else {
8354-
error(declaration, Diagnostics.Duplicate_number_index_signature);
8355-
}
8356-
}
8331+
if (declaration.parameters.length !== 1 || declaration === indexType.declaredNode) {
8332+
continue;
8333+
}
8334+
let type = getTypeFromIndexSignatureParameter(declaration.parameters[0])
8335+
if (type.flags & TypeFlags.String && indexType.kind === IndexKind.String) {
8336+
error(declaration, Diagnostics.Duplicate_string_index_signature);
8337+
}
8338+
else if (type.flags & TypeFlags.NumberLike && indexType.kind === IndexKind.Number) {
8339+
error(declaration, Diagnostics.Duplicate_number_index_signature);
83578340
}
83588341
}
83598342
}

src/compiler/types.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,8 +1527,11 @@ module ts {
15271527
kind: IndexKind // Kind of index
15281528
typeOfValue: Type // any
15291529
typeOfIndex?: Type // string|number|enum
1530-
declaredNode?: Declaration, // Declaration of [x: typeOfIndex]: typeOfValue
1531-
inherited?: Symbol // Symbol of baseType where inherited
1530+
1531+
// Useful for error reporting
1532+
declaredNode?: SignatureDeclaration, // Declaration of [x: typeOfIndex]: typeOfValue
1533+
declaredCount?: number // Number of declarations
1534+
inherited?: Symbol // Symbol of baseType where inherited
15321535
}
15331536

15341537
/* @internal */ // Resolved object or union type

tests/baselines/reference/APISample_compile.types

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ export function compile(fileNames: string[], options: ts.CompilerOptions): void
2323
>fileNames : string[], Symbol(fileNames, Decl(APISample_compile.ts, 13, 24))
2424
>options : ts.CompilerOptions, Symbol(options, Decl(APISample_compile.ts, 13, 44))
2525
>ts : any, Symbol(ts, Decl(APISample_compile.ts, 9, 20))
26-
>CompilerOptions : ts.CompilerOptions, Symbol(ts.CompilerOptions, Decl(typescript.d.ts, 1088, 5))
26+
>CompilerOptions : ts.CompilerOptions, Symbol(ts.CompilerOptions, Decl(typescript.d.ts, 1089, 5))
2727

2828
var program = ts.createProgram(fileNames, options);
2929
>program : ts.Program, Symbol(program, Decl(APISample_compile.ts, 14, 7))
3030
>ts.createProgram(fileNames, options) : ts.Program
31-
>ts.createProgram : (rootNames: string[], options: ts.CompilerOptions, host?: ts.CompilerHost) => ts.Program, Symbol(ts.createProgram, Decl(typescript.d.ts, 1215, 113))
31+
>ts.createProgram : (rootNames: string[], options: ts.CompilerOptions, host?: ts.CompilerHost) => ts.Program, Symbol(ts.createProgram, Decl(typescript.d.ts, 1216, 113))
3232
>ts : typeof ts, Symbol(ts, Decl(APISample_compile.ts, 9, 20))
33-
>createProgram : (rootNames: string[], options: ts.CompilerOptions, host?: ts.CompilerHost) => ts.Program, Symbol(ts.createProgram, Decl(typescript.d.ts, 1215, 113))
33+
>createProgram : (rootNames: string[], options: ts.CompilerOptions, host?: ts.CompilerHost) => ts.Program, Symbol(ts.createProgram, Decl(typescript.d.ts, 1216, 113))
3434
>fileNames : string[], Symbol(fileNames, Decl(APISample_compile.ts, 13, 24))
3535
>options : ts.CompilerOptions, Symbol(options, Decl(APISample_compile.ts, 13, 44))
3636

@@ -46,9 +46,9 @@ export function compile(fileNames: string[], options: ts.CompilerOptions): void
4646
>ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics) : ts.Diagnostic[]
4747
>ts.getPreEmitDiagnostics(program).concat : { <U extends ts.Diagnostic[]>(...items: U[]): ts.Diagnostic[]; (...items: ts.Diagnostic[]): ts.Diagnostic[]; }, Symbol(Array.concat, Decl(lib.d.ts, 1025, 13), Decl(lib.d.ts, 1030, 46))
4848
>ts.getPreEmitDiagnostics(program) : ts.Diagnostic[]
49-
>ts.getPreEmitDiagnostics : (program: ts.Program) => ts.Diagnostic[], Symbol(ts.getPreEmitDiagnostics, Decl(typescript.d.ts, 1213, 98))
49+
>ts.getPreEmitDiagnostics : (program: ts.Program) => ts.Diagnostic[], Symbol(ts.getPreEmitDiagnostics, Decl(typescript.d.ts, 1214, 98))
5050
>ts : typeof ts, Symbol(ts, Decl(APISample_compile.ts, 9, 20))
51-
>getPreEmitDiagnostics : (program: ts.Program) => ts.Diagnostic[], Symbol(ts.getPreEmitDiagnostics, Decl(typescript.d.ts, 1213, 98))
51+
>getPreEmitDiagnostics : (program: ts.Program) => ts.Diagnostic[], Symbol(ts.getPreEmitDiagnostics, Decl(typescript.d.ts, 1214, 98))
5252
>program : ts.Program, Symbol(program, Decl(APISample_compile.ts, 14, 7))
5353
>concat : { <U extends ts.Diagnostic[]>(...items: U[]): ts.Diagnostic[]; (...items: ts.Diagnostic[]): ts.Diagnostic[]; }, Symbol(Array.concat, Decl(lib.d.ts, 1025, 13), Decl(lib.d.ts, 1030, 46))
5454
>emitResult.diagnostics : ts.Diagnostic[], Symbol(ts.EmitResult.diagnostics, Decl(typescript.d.ts, 820, 29))
@@ -67,24 +67,24 @@ export function compile(fileNames: string[], options: ts.CompilerOptions): void
6767
>line : number, Symbol(line, Decl(APISample_compile.ts, 20, 13))
6868
>character : number, Symbol(character, Decl(APISample_compile.ts, 20, 19))
6969
>diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start) : ts.LineAndCharacter
70-
>diagnostic.file.getLineAndCharacterOfPosition : (pos: number) => ts.LineAndCharacter, Symbol(ts.SourceFile.getLineAndCharacterOfPosition, Decl(typescript.d.ts, 1277, 46))
71-
>diagnostic.file : ts.SourceFile, Symbol(ts.Diagnostic.file, Decl(typescript.d.ts, 1076, 26))
70+
>diagnostic.file.getLineAndCharacterOfPosition : (pos: number) => ts.LineAndCharacter, Symbol(ts.SourceFile.getLineAndCharacterOfPosition, Decl(typescript.d.ts, 1278, 46))
71+
>diagnostic.file : ts.SourceFile, Symbol(ts.Diagnostic.file, Decl(typescript.d.ts, 1077, 26))
7272
>diagnostic : ts.Diagnostic, Symbol(diagnostic, Decl(APISample_compile.ts, 19, 27))
73-
>file : ts.SourceFile, Symbol(ts.Diagnostic.file, Decl(typescript.d.ts, 1076, 26))
74-
>getLineAndCharacterOfPosition : (pos: number) => ts.LineAndCharacter, Symbol(ts.SourceFile.getLineAndCharacterOfPosition, Decl(typescript.d.ts, 1277, 46))
75-
>diagnostic.start : number, Symbol(ts.Diagnostic.start, Decl(typescript.d.ts, 1077, 25))
73+
>file : ts.SourceFile, Symbol(ts.Diagnostic.file, Decl(typescript.d.ts, 1077, 26))
74+
>getLineAndCharacterOfPosition : (pos: number) => ts.LineAndCharacter, Symbol(ts.SourceFile.getLineAndCharacterOfPosition, Decl(typescript.d.ts, 1278, 46))
75+
>diagnostic.start : number, Symbol(ts.Diagnostic.start, Decl(typescript.d.ts, 1078, 25))
7676
>diagnostic : ts.Diagnostic, Symbol(diagnostic, Decl(APISample_compile.ts, 19, 27))
77-
>start : number, Symbol(ts.Diagnostic.start, Decl(typescript.d.ts, 1077, 25))
77+
>start : number, Symbol(ts.Diagnostic.start, Decl(typescript.d.ts, 1078, 25))
7878

7979
var message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
8080
>message : string, Symbol(message, Decl(APISample_compile.ts, 21, 11))
8181
>ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n') : string
82-
>ts.flattenDiagnosticMessageText : (messageText: string | ts.DiagnosticMessageChain, newLine: string) => string, Symbol(ts.flattenDiagnosticMessageText, Decl(typescript.d.ts, 1214, 67))
82+
>ts.flattenDiagnosticMessageText : (messageText: string | ts.DiagnosticMessageChain, newLine: string) => string, Symbol(ts.flattenDiagnosticMessageText, Decl(typescript.d.ts, 1215, 67))
8383
>ts : typeof ts, Symbol(ts, Decl(APISample_compile.ts, 9, 20))
84-
>flattenDiagnosticMessageText : (messageText: string | ts.DiagnosticMessageChain, newLine: string) => string, Symbol(ts.flattenDiagnosticMessageText, Decl(typescript.d.ts, 1214, 67))
85-
>diagnostic.messageText : string | ts.DiagnosticMessageChain, Symbol(ts.Diagnostic.messageText, Decl(typescript.d.ts, 1079, 23))
84+
>flattenDiagnosticMessageText : (messageText: string | ts.DiagnosticMessageChain, newLine: string) => string, Symbol(ts.flattenDiagnosticMessageText, Decl(typescript.d.ts, 1215, 67))
85+
>diagnostic.messageText : string | ts.DiagnosticMessageChain, Symbol(ts.Diagnostic.messageText, Decl(typescript.d.ts, 1080, 23))
8686
>diagnostic : ts.Diagnostic, Symbol(diagnostic, Decl(APISample_compile.ts, 19, 27))
87-
>messageText : string | ts.DiagnosticMessageChain, Symbol(ts.Diagnostic.messageText, Decl(typescript.d.ts, 1079, 23))
87+
>messageText : string | ts.DiagnosticMessageChain, Symbol(ts.Diagnostic.messageText, Decl(typescript.d.ts, 1080, 23))
8888
>'\n' : string
8989

9090
console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
@@ -94,9 +94,9 @@ export function compile(fileNames: string[], options: ts.CompilerOptions): void
9494
>log : any
9595
>`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}` : string
9696
>diagnostic.file.fileName : string, Symbol(ts.SourceFile.fileName, Decl(typescript.d.ts, 743, 29))
97-
>diagnostic.file : ts.SourceFile, Symbol(ts.Diagnostic.file, Decl(typescript.d.ts, 1076, 26))
97+
>diagnostic.file : ts.SourceFile, Symbol(ts.Diagnostic.file, Decl(typescript.d.ts, 1077, 26))
9898
>diagnostic : ts.Diagnostic, Symbol(diagnostic, Decl(APISample_compile.ts, 19, 27))
99-
>file : ts.SourceFile, Symbol(ts.Diagnostic.file, Decl(typescript.d.ts, 1076, 26))
99+
>file : ts.SourceFile, Symbol(ts.Diagnostic.file, Decl(typescript.d.ts, 1077, 26))
100100
>fileName : string, Symbol(ts.SourceFile.fileName, Decl(typescript.d.ts, 743, 29))
101101
>line + 1 : number
102102
>line : number, Symbol(line, Decl(APISample_compile.ts, 20, 13))
@@ -153,16 +153,16 @@ compile(process.argv.slice(2), {
153153

154154
target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS
155155
>target : ts.ScriptTarget, Symbol(target, Decl(APISample_compile.ts, 31, 45))
156-
>ts.ScriptTarget.ES5 : ts.ScriptTarget, Symbol(ts.ScriptTarget.ES5, Decl(typescript.d.ts, 1131, 16))
157-
>ts.ScriptTarget : typeof ts.ScriptTarget, Symbol(ts.ScriptTarget, Decl(typescript.d.ts, 1129, 5))
156+
>ts.ScriptTarget.ES5 : ts.ScriptTarget, Symbol(ts.ScriptTarget.ES5, Decl(typescript.d.ts, 1132, 16))
157+
>ts.ScriptTarget : typeof ts.ScriptTarget, Symbol(ts.ScriptTarget, Decl(typescript.d.ts, 1130, 5))
158158
>ts : typeof ts, Symbol(ts, Decl(APISample_compile.ts, 9, 20))
159-
>ScriptTarget : typeof ts.ScriptTarget, Symbol(ts.ScriptTarget, Decl(typescript.d.ts, 1129, 5))
160-
>ES5 : ts.ScriptTarget, Symbol(ts.ScriptTarget.ES5, Decl(typescript.d.ts, 1131, 16))
159+
>ScriptTarget : typeof ts.ScriptTarget, Symbol(ts.ScriptTarget, Decl(typescript.d.ts, 1130, 5))
160+
>ES5 : ts.ScriptTarget, Symbol(ts.ScriptTarget.ES5, Decl(typescript.d.ts, 1132, 16))
161161
>module : ts.ModuleKind, Symbol(module, Decl(APISample_compile.ts, 32, 32))
162-
>ts.ModuleKind.CommonJS : ts.ModuleKind, Symbol(ts.ModuleKind.CommonJS, Decl(typescript.d.ts, 1122, 17))
163-
>ts.ModuleKind : typeof ts.ModuleKind, Symbol(ts.ModuleKind, Decl(typescript.d.ts, 1120, 5))
162+
>ts.ModuleKind.CommonJS : ts.ModuleKind, Symbol(ts.ModuleKind.CommonJS, Decl(typescript.d.ts, 1123, 17))
163+
>ts.ModuleKind : typeof ts.ModuleKind, Symbol(ts.ModuleKind, Decl(typescript.d.ts, 1121, 5))
164164
>ts : typeof ts, Symbol(ts, Decl(APISample_compile.ts, 9, 20))
165-
>ModuleKind : typeof ts.ModuleKind, Symbol(ts.ModuleKind, Decl(typescript.d.ts, 1120, 5))
166-
>CommonJS : ts.ModuleKind, Symbol(ts.ModuleKind.CommonJS, Decl(typescript.d.ts, 1122, 17))
165+
>ModuleKind : typeof ts.ModuleKind, Symbol(ts.ModuleKind, Decl(typescript.d.ts, 1121, 5))
166+
>CommonJS : ts.ModuleKind, Symbol(ts.ModuleKind.CommonJS, Decl(typescript.d.ts, 1123, 17))
167167

168168
});

0 commit comments

Comments
 (0)