Skip to content

Remove special any assignability for numeric index signatures #41660

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
2 changes: 1 addition & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18882,7 +18882,7 @@ namespace ts {
return indexTypesIdenticalTo(source, target, kind);
}
const targetType = getIndexTypeOfType(target, kind);
if (!targetType || targetType.flags & TypeFlags.Any && !sourceIsPrimitive) {
if (!targetType || targetType.flags & TypeFlags.Any && !sourceIsPrimitive && kind === IndexKind.String) {
// Index signature of type any permits assignment from everything but primitives
return Ternary.True;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
tests/cases/compiler/functionAssignabilityWithArrayLike01.ts(2,7): error TS2322: Type '() => void' is not assignable to type 'ArrayLike<any>'.
Index signature is missing in type '() => void'.


==== tests/cases/compiler/functionAssignabilityWithArrayLike01.ts (1 errors) ====
function func() {}
const array: ArrayLike<any> = func;
~~~~~
!!! error TS2322: Type '() => void' is not assignable to type 'ArrayLike<any>'.
!!! error TS2322: Index signature is missing in type '() => void'.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//// [functionAssignabilityWithArrayLike01.ts]
function func() {}
const array: ArrayLike<any> = func;

//// [functionAssignabilityWithArrayLike01.js]
function func() { }
var array = func;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
=== tests/cases/compiler/functionAssignabilityWithArrayLike01.ts ===
function func() {}
>func : Symbol(func, Decl(functionAssignabilityWithArrayLike01.ts, 0, 0))

const array: ArrayLike<any> = func;
>array : Symbol(array, Decl(functionAssignabilityWithArrayLike01.ts, 1, 5))
>ArrayLike : Symbol(ArrayLike, Decl(lib.es5.d.ts, --, --))
>func : Symbol(func, Decl(functionAssignabilityWithArrayLike01.ts, 0, 0))

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
=== tests/cases/compiler/functionAssignabilityWithArrayLike01.ts ===
function func() {}
>func : () => void

const array: ArrayLike<any> = func;
>array : ArrayLike<any>
>func : () => void

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
tests/cases/compiler/functionAssignabilityWithArrayLike01.ts(2,7): error TS2322: Type '() => void' is not assignable to type 'ArrayLike<any>'.
Index signature is missing in type '() => void'.


==== tests/cases/compiler/functionAssignabilityWithArrayLike01.ts (1 errors) ====
function func() {}
const array: ArrayLike<any> = func;
~~~~~
!!! error TS2322: Type '() => void' is not assignable to type 'ArrayLike<any>'.
!!! error TS2322: Index signature is missing in type '() => void'.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//// [functionAssignabilityWithArrayLike01.ts]
function func() {}
const array: ArrayLike<any> = func;

//// [functionAssignabilityWithArrayLike01.js]
"use strict";
function func() { }
var array = func;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
=== tests/cases/compiler/functionAssignabilityWithArrayLike01.ts ===
function func() {}
>func : Symbol(func, Decl(functionAssignabilityWithArrayLike01.ts, 0, 0))

const array: ArrayLike<any> = func;
>array : Symbol(array, Decl(functionAssignabilityWithArrayLike01.ts, 1, 5))
>ArrayLike : Symbol(ArrayLike, Decl(lib.es5.d.ts, --, --))
>func : Symbol(func, Decl(functionAssignabilityWithArrayLike01.ts, 0, 0))

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
=== tests/cases/compiler/functionAssignabilityWithArrayLike01.ts ===
function func() {}
>func : () => void

const array: ArrayLike<any> = func;
>array : ArrayLike<any>
>func : () => void

34 changes: 0 additions & 34 deletions tests/baselines/reference/subclassThisTypeAssignable.errors.txt

This file was deleted.

63 changes: 63 additions & 0 deletions tests/baselines/reference/subclassThisTypeAssignable01.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
tests/cases/compiler/file1.js(2,7): error TS2322: Type 'C' is not assignable to type 'ClassComponent<any>'.
tests/cases/compiler/tile1.ts(2,30): error TS2344: Type 'State' does not satisfy the constraint 'Lifecycle<Attrs, State>'.
tests/cases/compiler/tile1.ts(6,81): error TS2744: Type parameter defaults can only reference previously declared type parameters.
tests/cases/compiler/tile1.ts(11,40): error TS2344: Type 'State' does not satisfy the constraint 'Lifecycle<Attrs, State>'.
tests/cases/compiler/tile1.ts(21,2): error TS2416: Property 'view' in type 'C' is not assignable to the same property in base type 'ClassComponent<MyAttrs>'.
Type '(v: Vnode<MyAttrs, Lifecycle<MyAttrs, any>>) => number' is not assignable to type '(vnode: Vnode<MyAttrs, this>) => number'.
Types of parameters 'v' and 'vnode' are incompatible.
Type 'Vnode<MyAttrs, this>' is not assignable to type 'Vnode<MyAttrs, Lifecycle<MyAttrs, any>>'.
Type 'this' is not assignable to type 'Lifecycle<MyAttrs, any>'.
Type 'C' is not assignable to type 'Lifecycle<MyAttrs, any>'.
Index signature is missing in type 'C'.
tests/cases/compiler/tile1.ts(24,7): error TS2322: Type 'C' is not assignable to type 'ClassComponent<any>'.
Index signature is missing in type 'C'.


==== tests/cases/compiler/tile1.ts (5 errors) ====
interface Lifecycle<Attrs, State> {
oninit?(vnode: Vnode<Attrs, State>): number;
~~~~~
!!! error TS2344: Type 'State' does not satisfy the constraint 'Lifecycle<Attrs, State>'.
[_: number]: any;
}

interface Vnode<Attrs, State extends Lifecycle<Attrs, State> = Lifecycle<Attrs, State>> {
~~~~~
!!! error TS2744: Type parameter defaults can only reference previously declared type parameters.
tag: Component<Attrs, State>;
}

interface Component<Attrs, State> {
view(this: State, vnode: Vnode<Attrs, State>): number;
~~~~~
!!! error TS2344: Type 'State' does not satisfy the constraint 'Lifecycle<Attrs, State>'.
}

interface ClassComponent<A> extends Lifecycle<A, ClassComponent<A>> {
oninit?(vnode: Vnode<A, this>): number;
view(vnode: Vnode<A, this>): number;
}

interface MyAttrs { id: number }
class C implements ClassComponent<MyAttrs> {
view(v: Vnode<MyAttrs>) { return 0; }
~~~~
!!! error TS2416: Property 'view' in type 'C' is not assignable to the same property in base type 'ClassComponent<MyAttrs>'.
!!! error TS2416: Type '(v: Vnode<MyAttrs, Lifecycle<MyAttrs, any>>) => number' is not assignable to type '(vnode: Vnode<MyAttrs, this>) => number'.
!!! error TS2416: Types of parameters 'v' and 'vnode' are incompatible.
!!! error TS2416: Type 'Vnode<MyAttrs, this>' is not assignable to type 'Vnode<MyAttrs, Lifecycle<MyAttrs, any>>'.
!!! error TS2416: Type 'this' is not assignable to type 'Lifecycle<MyAttrs, any>'.
!!! error TS2416: Type 'C' is not assignable to type 'Lifecycle<MyAttrs, any>'.
!!! error TS2416: Index signature is missing in type 'C'.
}

const test8: ClassComponent<any> = new C();
~~~~~
!!! error TS2322: Type 'C' is not assignable to type 'ClassComponent<any>'.
!!! error TS2322: Index signature is missing in type 'C'.
==== tests/cases/compiler/file1.js (1 errors) ====
/** @type {ClassComponent<any>} */
const test9 = new C();
~~~~~
!!! error TS2322: Type 'C' is not assignable to type 'ClassComponent<any>'.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//// [tests/cases/compiler/subclassThisTypeAssignable.ts] ////
//// [tests/cases/compiler/subclassThisTypeAssignable01.ts] ////

//// [tile1.ts]
interface Lifecycle<Attrs, State> {
Expand Down
49 changes: 49 additions & 0 deletions tests/baselines/reference/subclassThisTypeAssignable02.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//// [tests/cases/compiler/subclassThisTypeAssignable02.ts] ////

//// [tile1.ts]
interface Lifecycle<Attrs, State extends Lifecycle<Attrs, State>> {
oninit?(vnode: Vnode<Attrs, State>): number;
[_: number]: any;
}

interface Vnode<Attrs, State extends Lifecycle<Attrs, State>> {
tag: Component<Attrs, State>;
}

interface Component<Attrs, State extends Lifecycle<Attrs, State>> {
view(this: State, vnode: Vnode<Attrs, State>): number;
}

interface ClassComponent<A> extends Lifecycle<A, ClassComponent<A>> {
oninit?(vnode: Vnode<A, this>): number;
view(vnode: Vnode<A, this>): number;
}

interface MyAttrs { id: number }
class C implements ClassComponent<MyAttrs> {
view(v: Vnode<MyAttrs, C>) { return 0; }

// Must declare a compatible-ish index signature or else
// we won't correctly implement ClassComponent.
[_: number]: unknown;
}

const test8: ClassComponent<any> = new C();
//// [file1.js]
/** @type {ClassComponent<any>} */
const test9 = new C();


//// [tile1.js]
"use strict";
var C = /** @class */ (function () {
function C() {
}
C.prototype.view = function (v) { return 0; };
return C;
}());
var test8 = new C();
//// [file1.js]
"use strict";
/** @type {ClassComponent<any>} */
var test9 = new C();
107 changes: 107 additions & 0 deletions tests/baselines/reference/subclassThisTypeAssignable02.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
=== tests/cases/compiler/tile1.ts ===
interface Lifecycle<Attrs, State extends Lifecycle<Attrs, State>> {
>Lifecycle : Symbol(Lifecycle, Decl(tile1.ts, 0, 0))
>Attrs : Symbol(Attrs, Decl(tile1.ts, 0, 20))
>State : Symbol(State, Decl(tile1.ts, 0, 26))
>Lifecycle : Symbol(Lifecycle, Decl(tile1.ts, 0, 0))
>Attrs : Symbol(Attrs, Decl(tile1.ts, 0, 20))
>State : Symbol(State, Decl(tile1.ts, 0, 26))

oninit?(vnode: Vnode<Attrs, State>): number;
>oninit : Symbol(Lifecycle.oninit, Decl(tile1.ts, 0, 67))
>vnode : Symbol(vnode, Decl(tile1.ts, 1, 9))
>Vnode : Symbol(Vnode, Decl(tile1.ts, 3, 1))
>Attrs : Symbol(Attrs, Decl(tile1.ts, 0, 20))
>State : Symbol(State, Decl(tile1.ts, 0, 26))

[_: number]: any;
>_ : Symbol(_, Decl(tile1.ts, 2, 2))
}

interface Vnode<Attrs, State extends Lifecycle<Attrs, State>> {
>Vnode : Symbol(Vnode, Decl(tile1.ts, 3, 1))
>Attrs : Symbol(Attrs, Decl(tile1.ts, 5, 16))
>State : Symbol(State, Decl(tile1.ts, 5, 22))
>Lifecycle : Symbol(Lifecycle, Decl(tile1.ts, 0, 0))
>Attrs : Symbol(Attrs, Decl(tile1.ts, 5, 16))
>State : Symbol(State, Decl(tile1.ts, 5, 22))

tag: Component<Attrs, State>;
>tag : Symbol(Vnode.tag, Decl(tile1.ts, 5, 63))
>Component : Symbol(Component, Decl(tile1.ts, 7, 1))
>Attrs : Symbol(Attrs, Decl(tile1.ts, 5, 16))
>State : Symbol(State, Decl(tile1.ts, 5, 22))
}

interface Component<Attrs, State extends Lifecycle<Attrs, State>> {
>Component : Symbol(Component, Decl(tile1.ts, 7, 1))
>Attrs : Symbol(Attrs, Decl(tile1.ts, 9, 20))
>State : Symbol(State, Decl(tile1.ts, 9, 26))
>Lifecycle : Symbol(Lifecycle, Decl(tile1.ts, 0, 0))
>Attrs : Symbol(Attrs, Decl(tile1.ts, 9, 20))
>State : Symbol(State, Decl(tile1.ts, 9, 26))

view(this: State, vnode: Vnode<Attrs, State>): number;
>view : Symbol(Component.view, Decl(tile1.ts, 9, 67))
>this : Symbol(this, Decl(tile1.ts, 10, 6))
>State : Symbol(State, Decl(tile1.ts, 9, 26))
>vnode : Symbol(vnode, Decl(tile1.ts, 10, 18))
>Vnode : Symbol(Vnode, Decl(tile1.ts, 3, 1))
>Attrs : Symbol(Attrs, Decl(tile1.ts, 9, 20))
>State : Symbol(State, Decl(tile1.ts, 9, 26))
}

interface ClassComponent<A> extends Lifecycle<A, ClassComponent<A>> {
>ClassComponent : Symbol(ClassComponent, Decl(tile1.ts, 11, 1))
>A : Symbol(A, Decl(tile1.ts, 13, 25))
>Lifecycle : Symbol(Lifecycle, Decl(tile1.ts, 0, 0))
>A : Symbol(A, Decl(tile1.ts, 13, 25))
>ClassComponent : Symbol(ClassComponent, Decl(tile1.ts, 11, 1))
>A : Symbol(A, Decl(tile1.ts, 13, 25))

oninit?(vnode: Vnode<A, this>): number;
>oninit : Symbol(ClassComponent.oninit, Decl(tile1.ts, 13, 69))
>vnode : Symbol(vnode, Decl(tile1.ts, 14, 9))
>Vnode : Symbol(Vnode, Decl(tile1.ts, 3, 1))
>A : Symbol(A, Decl(tile1.ts, 13, 25))

view(vnode: Vnode<A, this>): number;
>view : Symbol(ClassComponent.view, Decl(tile1.ts, 14, 40))
>vnode : Symbol(vnode, Decl(tile1.ts, 15, 6))
>Vnode : Symbol(Vnode, Decl(tile1.ts, 3, 1))
>A : Symbol(A, Decl(tile1.ts, 13, 25))
}

interface MyAttrs { id: number }
>MyAttrs : Symbol(MyAttrs, Decl(tile1.ts, 16, 1))
>id : Symbol(MyAttrs.id, Decl(tile1.ts, 18, 19))

class C implements ClassComponent<MyAttrs> {
>C : Symbol(C, Decl(tile1.ts, 18, 32))
>ClassComponent : Symbol(ClassComponent, Decl(tile1.ts, 11, 1))
>MyAttrs : Symbol(MyAttrs, Decl(tile1.ts, 16, 1))

view(v: Vnode<MyAttrs, C>) { return 0; }
>view : Symbol(C.view, Decl(tile1.ts, 19, 44))
>v : Symbol(v, Decl(tile1.ts, 20, 6))
>Vnode : Symbol(Vnode, Decl(tile1.ts, 3, 1))
>MyAttrs : Symbol(MyAttrs, Decl(tile1.ts, 16, 1))
>C : Symbol(C, Decl(tile1.ts, 18, 32))

// Must declare a compatible-ish index signature or else
// we won't correctly implement ClassComponent.
[_: number]: unknown;
>_ : Symbol(_, Decl(tile1.ts, 24, 2))
}

const test8: ClassComponent<any> = new C();
>test8 : Symbol(test8, Decl(tile1.ts, 27, 5))
>ClassComponent : Symbol(ClassComponent, Decl(tile1.ts, 11, 1))
>C : Symbol(C, Decl(tile1.ts, 18, 32))

=== tests/cases/compiler/file1.js ===
/** @type {ClassComponent<any>} */
const test9 = new C();
>test9 : Symbol(test9, Decl(file1.js, 1, 5))
>C : Symbol(C, Decl(tile1.ts, 18, 32))

Loading