diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 219f370fd5..992c609929 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7030,13 +7030,11 @@ namespace ts { function getIntendedTypeFromJSDocTypeReference(node: TypeReferenceNode): Type { if (isIdentifier(node.typeName)) { if (node.typeName.escapedText === "Object") { - if (node.typeArguments && node.typeArguments.length === 2) { + if (isJSDocIndexSignature(node)) { const indexed = getTypeFromTypeNode(node.typeArguments[0]); const target = getTypeFromTypeNode(node.typeArguments[1]); const index = createIndexInfo(target, /*isReadonly*/ false); - if (indexed === stringType || indexed === numberType) { - return createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, indexed === stringType && index, indexed === numberType && index); - } + return createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, indexed === stringType && index, indexed === numberType && index); } return anyType; } @@ -7066,6 +7064,14 @@ namespace ts { } } + function isJSDocIndexSignature(node: TypeReferenceNode | ExpressionWithTypeArguments) { + return isTypeReferenceNode(node) && + isIdentifier(node.typeName) && + node.typeName.escapedText === "Object" && + node.typeArguments && node.typeArguments.length === 2 && + (node.typeArguments[0].kind === SyntaxKind.StringKeyword || node.typeArguments[0].kind === SyntaxKind.NumberKeyword); + } + function getTypeFromJSDocNullableTypeNode(node: JSDocNullableType) { const type = getTypeFromTypeNode(node.type); return strictNullChecks ? getUnionType([type, nullType]) : type; @@ -19179,7 +19185,10 @@ namespace ts { // There is no resolved symbol cached if the type resolved to a builtin // via JSDoc type reference resolution (eg, Boolean became boolean), none // of which are generic when they have no associated symbol - error(node, Diagnostics.Type_0_is_not_generic, typeToString(type)); + // (additionally, JSDoc's index signature syntax, Object actually uses generic syntax without being generic) + if (!isJSDocIndexSignature(node)) { + error(node, Diagnostics.Type_0_is_not_generic, typeToString(type)); + } return; } let typeParameters = symbol.flags & SymbolFlags.TypeAlias && getSymbolLinks(symbol).typeParameters; diff --git a/tests/baselines/reference/jsdocIndexSignature.errors.txt b/tests/baselines/reference/jsdocIndexSignature.errors.txt new file mode 100644 index 0000000000..de336795b3 --- /dev/null +++ b/tests/baselines/reference/jsdocIndexSignature.errors.txt @@ -0,0 +1,18 @@ +tests/cases/conformance/jsdoc/indices.js(9,5): error TS2322: Type '1' is not assignable to type 'boolean'. + + +==== tests/cases/conformance/jsdoc/indices.js (1 errors) ==== + /** @type {Object.} */ + var o1; + /** @type {Object.} */ + var o2; + /** @type {Object.} */ + var o3; + /** @param {Object.} o */ + function f(o) { + o.foo = 1; // error + ~~~~~ +!!! error TS2322: Type '1' is not assignable to type 'boolean'. + o.bar = false; // ok + } + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocIndexSignature.symbols b/tests/baselines/reference/jsdocIndexSignature.symbols index 8814fc18b6..89288458f4 100644 --- a/tests/baselines/reference/jsdocIndexSignature.symbols +++ b/tests/baselines/reference/jsdocIndexSignature.symbols @@ -11,3 +11,15 @@ var o2; var o3; >o3 : Symbol(o3, Decl(indices.js, 5, 3)) +/** @param {Object.} o */ +function f(o) { +>f : Symbol(f, Decl(indices.js, 5, 7)) +>o : Symbol(o, Decl(indices.js, 7, 11)) + + o.foo = 1; // error +>o : Symbol(o, Decl(indices.js, 7, 11)) + + o.bar = false; // ok +>o : Symbol(o, Decl(indices.js, 7, 11)) +} + diff --git a/tests/baselines/reference/jsdocIndexSignature.types b/tests/baselines/reference/jsdocIndexSignature.types index 4c1feaa272..d9ac826309 100644 --- a/tests/baselines/reference/jsdocIndexSignature.types +++ b/tests/baselines/reference/jsdocIndexSignature.types @@ -11,3 +11,23 @@ var o2; var o3; >o3 : any +/** @param {Object.} o */ +function f(o) { +>f : (o: { [x: string]: boolean; }) => void +>o : { [x: string]: boolean; } + + o.foo = 1; // error +>o.foo = 1 : 1 +>o.foo : boolean +>o : { [x: string]: boolean; } +>foo : boolean +>1 : 1 + + o.bar = false; // ok +>o.bar = false : false +>o.bar : boolean +>o : { [x: string]: boolean; } +>bar : boolean +>false : false +} +