From 1045d95a44a64560b8e0b6d36fe03a5de1183e2a Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 1 Dec 2017 21:09:06 -0500 Subject: [PATCH] Always instantiate the extends clause, even in the presence of an error (#20232) * Still instantiate the extends clause even when theres a noimplicitany error in js * Only be permissive for JS * In JS, instantiate classes even when they have too many type arguments, instead of returning unknownType --- src/compiler/checker.ts | 12 ++++-- .../reference/jsExtendsImplicitAny.errors.txt | 16 ++++++-- .../reference/jsExtendsImplicitAny.symbols | 27 +++++++++++-- .../reference/jsExtendsImplicitAny.types | 28 ++++++++++++- ...itAnyNoCascadingReferenceErrors.errors.txt | 19 +++++++++ ...NoImplicitAnyNoCascadingReferenceErrors.js | 40 +++++++++++++++++++ ...licitAnyNoCascadingReferenceErrors.symbols | 28 +++++++++++++ ...mplicitAnyNoCascadingReferenceErrors.types | 33 +++++++++++++++ tests/cases/compiler/jsExtendsImplicitAny.ts | 8 +++- ...NoImplicitAnyNoCascadingReferenceErrors.ts | 17 ++++++++ 10 files changed, 214 insertions(+), 14 deletions(-) create mode 100644 tests/baselines/reference/jsNoImplicitAnyNoCascadingReferenceErrors.errors.txt create mode 100644 tests/baselines/reference/jsNoImplicitAnyNoCascadingReferenceErrors.js create mode 100644 tests/baselines/reference/jsNoImplicitAnyNoCascadingReferenceErrors.symbols create mode 100644 tests/baselines/reference/jsNoImplicitAnyNoCascadingReferenceErrors.types create mode 100644 tests/cases/compiler/jsNoImplicitAnyNoCascadingReferenceErrors.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f89e1f4939..c3def69c1e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5817,7 +5817,7 @@ namespace ts { for (const baseSig of baseSignatures) { const minTypeArgumentCount = getMinTypeArgumentCount(baseSig.typeParameters); const typeParamCount = length(baseSig.typeParameters); - if ((isJavaScript || typeArgCount >= minTypeArgumentCount) && typeArgCount <= typeParamCount) { + if (isJavaScript || (typeArgCount >= minTypeArgumentCount && typeArgCount <= typeParamCount)) { const sig = typeParamCount ? createSignatureInstantiation(baseSig, fillMissingTypeArguments(typeArguments, baseSig.typeParameters, minTypeArgumentCount, isJavaScript)) : cloneSignature(baseSig); sig.typeParameters = classType.localTypeParameters; sig.resolvedReturnType = classType; @@ -6718,7 +6718,7 @@ namespace ts { const numTypeParameters = length(typeParameters); if (numTypeParameters) { const numTypeArguments = length(typeArguments); - if ((isJavaScriptImplicitAny || numTypeArguments >= minTypeArgumentCount) && numTypeArguments <= numTypeParameters) { + if (isJavaScriptImplicitAny || (numTypeArguments >= minTypeArgumentCount && numTypeArguments <= numTypeParameters)) { if (!typeArguments) { typeArguments = []; } @@ -6737,6 +6737,7 @@ namespace ts { } typeArguments[i] = defaultType ? instantiateType(defaultType, mapper) : getDefaultTypeArgumentType(isJavaScriptImplicitAny); } + typeArguments.length = typeParameters.length; } } return typeArguments; @@ -7208,12 +7209,15 @@ namespace ts { : Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments; const typeStr = typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType); error(node, diag, typeStr, minTypeArgumentCount, typeParameters.length); - return unknownType; + if (!isJs) { + // TODO: Adopt same permissive behavior in TS as in JS to reduce follow-on editing experience failures (requires editing fillMissingTypeArguments) + return unknownType; + } } // In a type reference, the outer type parameters of the referenced class or interface are automatically // supplied as type arguments and the type reference only specifies arguments for the local type parameters // of the class or interface. - const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(typeArgs, typeParameters, minTypeArgumentCount, isJsImplicitAny)); + const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(typeArgs, typeParameters, minTypeArgumentCount, isJs)); return createTypeReference(type, typeArguments); } if (node.typeArguments) { diff --git a/tests/baselines/reference/jsExtendsImplicitAny.errors.txt b/tests/baselines/reference/jsExtendsImplicitAny.errors.txt index 4b836970e0..63f96a1aab 100644 --- a/tests/baselines/reference/jsExtendsImplicitAny.errors.txt +++ b/tests/baselines/reference/jsExtendsImplicitAny.errors.txt @@ -1,17 +1,25 @@ /b.js(1,17): error TS8026: Expected A type arguments; provide these with an '@extends' tag. -/b.js(3,15): error TS2314: Generic type 'A' requires 1 type argument(s). +/b.js(4,15): error TS2314: Generic type 'A' requires 1 type argument(s). +/b.js(8,15): error TS2314: Generic type 'A' requires 1 type argument(s). ==== /a.d.ts (0 errors) ==== declare class A { x: T; } -==== /b.js (2 errors) ==== +==== /b.js (3 errors) ==== class B extends A {} ~ !!! error TS8026: Expected A type arguments; provide these with an '@extends' tag. + new B().x; /** @augments A */ ~ !!! error TS2314: Generic type 'A' requires 1 type argument(s). - class C { } - \ No newline at end of file + class C extends A { } + new C().x; + + /** @augments A */ + ~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2314: Generic type 'A' requires 1 type argument(s). + class D extends A {} + new D().x; \ No newline at end of file diff --git a/tests/baselines/reference/jsExtendsImplicitAny.symbols b/tests/baselines/reference/jsExtendsImplicitAny.symbols index 0a6d71e109..4c378d3033 100644 --- a/tests/baselines/reference/jsExtendsImplicitAny.symbols +++ b/tests/baselines/reference/jsExtendsImplicitAny.symbols @@ -10,7 +10,28 @@ class B extends A {} >B : Symbol(B, Decl(b.js, 0, 0)) >A : Symbol(A, Decl(a.d.ts, 0, 0)) -/** @augments A */ -class C { } ->C : Symbol(C, Decl(b.js, 0, 20)) +new B().x; +>new B().x : Symbol(A.x, Decl(a.d.ts, 0, 20)) +>B : Symbol(B, Decl(b.js, 0, 0)) +>x : Symbol(A.x, Decl(a.d.ts, 0, 20)) + +/** @augments A */ +class C extends A { } +>C : Symbol(C, Decl(b.js, 1, 10)) +>A : Symbol(A, Decl(a.d.ts, 0, 0)) + +new C().x; +>new C().x : Symbol(A.x, Decl(a.d.ts, 0, 20)) +>C : Symbol(C, Decl(b.js, 1, 10)) +>x : Symbol(A.x, Decl(a.d.ts, 0, 20)) + +/** @augments A */ +class D extends A {} +>D : Symbol(D, Decl(b.js, 5, 10)) +>A : Symbol(A, Decl(a.d.ts, 0, 0)) + +new D().x; +>new D().x : Symbol(A.x, Decl(a.d.ts, 0, 20)) +>D : Symbol(D, Decl(b.js, 5, 10)) +>x : Symbol(A.x, Decl(a.d.ts, 0, 20)) diff --git a/tests/baselines/reference/jsExtendsImplicitAny.types b/tests/baselines/reference/jsExtendsImplicitAny.types index ba98ef3ed2..9e53d57da9 100644 --- a/tests/baselines/reference/jsExtendsImplicitAny.types +++ b/tests/baselines/reference/jsExtendsImplicitAny.types @@ -8,9 +8,33 @@ declare class A { x: T; } === /b.js === class B extends A {} >B : B ->A : typeof A +>A : A + +new B().x; +>new B().x : any +>new B() : B +>B : typeof B +>x : any /** @augments A */ -class C { } +class C extends A { } >C : C +>A : A + +new C().x; +>new C().x : any +>new C() : C +>C : typeof C +>x : any + +/** @augments A */ +class D extends A {} +>D : D +>A : A + +new D().x; +>new D().x : number +>new D() : D +>D : typeof D +>x : number diff --git a/tests/baselines/reference/jsNoImplicitAnyNoCascadingReferenceErrors.errors.txt b/tests/baselines/reference/jsNoImplicitAnyNoCascadingReferenceErrors.errors.txt new file mode 100644 index 0000000000..cc04d17b2d --- /dev/null +++ b/tests/baselines/reference/jsNoImplicitAnyNoCascadingReferenceErrors.errors.txt @@ -0,0 +1,19 @@ +tests/cases/compiler/index.js(3,21): error TS8026: Expected Foo type arguments; provide these with an '@extends' tag. + + +==== tests/cases/compiler/somelib.d.ts (0 errors) ==== + export declare class Foo { + prop: T; + } +==== tests/cases/compiler/index.js (1 errors) ==== + import {Foo} from "./somelib"; + + class MyFoo extends Foo { + ~~~ +!!! error TS8026: Expected Foo type arguments; provide these with an '@extends' tag. + constructor() { + super(); + this.prop.alpha = 12; + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/jsNoImplicitAnyNoCascadingReferenceErrors.js b/tests/baselines/reference/jsNoImplicitAnyNoCascadingReferenceErrors.js new file mode 100644 index 0000000000..c2c3de5b83 --- /dev/null +++ b/tests/baselines/reference/jsNoImplicitAnyNoCascadingReferenceErrors.js @@ -0,0 +1,40 @@ +//// [tests/cases/compiler/jsNoImplicitAnyNoCascadingReferenceErrors.ts] //// + +//// [somelib.d.ts] +export declare class Foo { + prop: T; +} +//// [index.js] +import {Foo} from "./somelib"; + +class MyFoo extends Foo { + constructor() { + super(); + this.prop.alpha = 12; + } +} + + +//// [index.js] +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +exports.__esModule = true; +var somelib_1 = require("./somelib"); +var MyFoo = /** @class */ (function (_super) { + __extends(MyFoo, _super); + function MyFoo() { + var _this = _super.call(this) || this; + _this.prop.alpha = 12; + return _this; + } + return MyFoo; +}(somelib_1.Foo)); diff --git a/tests/baselines/reference/jsNoImplicitAnyNoCascadingReferenceErrors.symbols b/tests/baselines/reference/jsNoImplicitAnyNoCascadingReferenceErrors.symbols new file mode 100644 index 0000000000..fadd65b64b --- /dev/null +++ b/tests/baselines/reference/jsNoImplicitAnyNoCascadingReferenceErrors.symbols @@ -0,0 +1,28 @@ +=== tests/cases/compiler/somelib.d.ts === +export declare class Foo { +>Foo : Symbol(Foo, Decl(somelib.d.ts, --, --)) +>T : Symbol(T, Decl(somelib.d.ts, --, --)) + + prop: T; +>prop : Symbol(Foo.prop, Decl(somelib.d.ts, --, --)) +>T : Symbol(T, Decl(somelib.d.ts, --, --)) +} +=== tests/cases/compiler/index.js === +import {Foo} from "./somelib"; +>Foo : Symbol(Foo, Decl(index.js, 0, 8)) + +class MyFoo extends Foo { +>MyFoo : Symbol(MyFoo, Decl(index.js, 0, 30)) +>Foo : Symbol(Foo, Decl(index.js, 0, 8)) + + constructor() { + super(); +>super : Symbol(Foo, Decl(somelib.d.ts, --, --)) + + this.prop.alpha = 12; +>this.prop : Symbol(Foo.prop, Decl(somelib.d.ts, --, --)) +>this : Symbol(MyFoo, Decl(index.js, 0, 30)) +>prop : Symbol(Foo.prop, Decl(somelib.d.ts, --, --)) + } +} + diff --git a/tests/baselines/reference/jsNoImplicitAnyNoCascadingReferenceErrors.types b/tests/baselines/reference/jsNoImplicitAnyNoCascadingReferenceErrors.types new file mode 100644 index 0000000000..2f69fbe8bb --- /dev/null +++ b/tests/baselines/reference/jsNoImplicitAnyNoCascadingReferenceErrors.types @@ -0,0 +1,33 @@ +=== tests/cases/compiler/somelib.d.ts === +export declare class Foo { +>Foo : Foo +>T : T + + prop: T; +>prop : T +>T : T +} +=== tests/cases/compiler/index.js === +import {Foo} from "./somelib"; +>Foo : typeof Foo + +class MyFoo extends Foo { +>MyFoo : MyFoo +>Foo : Foo + + constructor() { + super(); +>super() : void +>super : typeof Foo + + this.prop.alpha = 12; +>this.prop.alpha = 12 : 12 +>this.prop.alpha : any +>this.prop : any +>this : this +>prop : any +>alpha : any +>12 : 12 + } +} + diff --git a/tests/cases/compiler/jsExtendsImplicitAny.ts b/tests/cases/compiler/jsExtendsImplicitAny.ts index e3d737ceb3..e847953c3e 100644 --- a/tests/cases/compiler/jsExtendsImplicitAny.ts +++ b/tests/cases/compiler/jsExtendsImplicitAny.ts @@ -8,6 +8,12 @@ declare class A { x: T; } // @Filename: /b.js class B extends A {} +new B().x; /** @augments A */ -class C { } +class C extends A { } +new C().x; + +/** @augments A */ +class D extends A {} +new D().x; \ No newline at end of file diff --git a/tests/cases/compiler/jsNoImplicitAnyNoCascadingReferenceErrors.ts b/tests/cases/compiler/jsNoImplicitAnyNoCascadingReferenceErrors.ts new file mode 100644 index 0000000000..3ccd178d9f --- /dev/null +++ b/tests/cases/compiler/jsNoImplicitAnyNoCascadingReferenceErrors.ts @@ -0,0 +1,17 @@ +// @allowJs: true +// @checkJs: true +// @noImplicitAny: true +// @outDir: ./built +// @filename: somelib.d.ts +export declare class Foo { + prop: T; +} +// @filename: index.js +import {Foo} from "./somelib"; + +class MyFoo extends Foo { + constructor() { + super(); + this.prop.alpha = 12; + } +}