Make the unconstrained type parameter and {} assignability rule not apply under strictNullChecks (#48366)

* Make the unconstrained type parameter and {} assignability rule not apply under strictNullChecks

* Fix lint, PR feedback
This commit is contained in:
Wesley Wigham
2022-03-23 16:22:40 -07:00
committed by GitHub
parent b5a3a058f5
commit 8cd4b7a5da
13 changed files with 819 additions and 25 deletions
+6 -3
View File
@@ -18702,6 +18702,9 @@ namespace ts {
return;
}
reportRelationError(headMessage, source, target);
if (strictNullChecks && source.flags & TypeFlags.TypeVariable && source.symbol?.declarations?.[0] && !getConstraintOfType(source as TypeVariable) && isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive))) {
associateRelatedInfo(createDiagnosticForNode(source.symbol.declarations[0], Diagnostics.This_type_parameter_probably_needs_an_extends_object_constraint));
}
}
function traceUnionsOrIntersectionsTooLarge(source: Type, target: Type): void {
@@ -19492,7 +19495,7 @@ namespace ts {
// IndexedAccess comparisons are handled above in the `targetFlags & TypeFlage.IndexedAccess` branch
if (!(sourceFlags & TypeFlags.IndexedAccess && targetFlags & TypeFlags.IndexedAccess)) {
const constraint = getConstraintOfType(source as TypeVariable);
if (!constraint || (sourceFlags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.Any)) {
if (!strictNullChecks && (!constraint || (sourceFlags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.Any))) {
// A type variable with no constraint is not related to the non-primitive object type.
if (result = isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive), RecursionFlags.Both)) {
resetErrorInfo(saveErrorInfo);
@@ -19500,12 +19503,12 @@ namespace ts {
}
}
// hi-speed no-this-instantiation check (less accurate, but avoids costly `this`-instantiation when the constraint will suffice), see #28231 for report on why this is needed
else if (result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState)) {
else if (constraint && (result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState))) {
resetErrorInfo(saveErrorInfo);
return result;
}
// slower, fuller, this-instantiated check (necessary when comparing raw `this` types from base classes), see `subclassWithPolymorphicThisIsAssignable.ts` test for example
else if (result = isRelatedTo(getTypeWithThisArgument(constraint, source), target, RecursionFlags.Source, reportErrors && !(targetFlags & sourceFlags & TypeFlags.TypeParameter), /*headMessage*/ undefined, intersectionState)) {
else if (constraint && (result = isRelatedTo(getTypeWithThisArgument(constraint, source), target, RecursionFlags.Source, reportErrors && !(targetFlags & sourceFlags & TypeFlags.TypeParameter), /*headMessage*/ undefined, intersectionState))) {
resetErrorInfo(saveErrorInfo);
return result;
}
+3 -3
View File
@@ -301,7 +301,7 @@ namespace ts {
array.length = outIndex;
}
export function clear(array: {}[]): void {
export function clear(array: unknown[]): void {
array.length = 0;
}
@@ -1644,7 +1644,7 @@ namespace ts {
/**
* Tests whether a value is an array.
*/
export function isArray(value: any): value is readonly {}[] {
export function isArray(value: any): value is readonly unknown[] {
return Array.isArray ? Array.isArray(value) : value instanceof Array;
}
@@ -1677,7 +1677,7 @@ namespace ts {
}
/** Does nothing. */
export function noop(_?: {} | null | undefined): void { }
export function noop(_?: unknown): void { }
/** Do nothing and return false */
export function returnFalse(): false {
+4
View File
@@ -1506,6 +1506,10 @@
"category": "Error",
"code": 2207
},
"This type parameter probably needs an `extends object` constraint.": {
"category": "Error",
"code": 2208
},
"Duplicate identifier '{0}'.": {
"category": "Error",
+2 -2
View File
@@ -20,8 +20,8 @@ globalThis.assert = _chai.assert;
}
assertDeepImpl(a, b, msg);
function arrayExtraKeysObject(a: readonly ({} | null | undefined)[]): object {
const obj: { [key: string]: {} | null | undefined } = {};
function arrayExtraKeysObject(a: readonly unknown[]): object {
const obj: { [key: string]: unknown } = {};
for (const key in a) {
if (Number.isNaN(Number(key))) {
obj[key] = a[key];
+1 -1
View File
@@ -554,7 +554,7 @@ namespace ts {
}
}
function simpleForwardCall(logger: Logger, actionDescription: string, action: () => {}, logPerformance: boolean): {} {
function simpleForwardCall(logger: Logger, actionDescription: string, action: () => unknown, logPerformance: boolean): unknown {
let start: number | undefined;
if (logPerformance) {
logger.log(actionDescription);
@@ -23,7 +23,7 @@ function boxify<T>(obj: T): Boxified<T> {
return result;
}
function unboxify<T>(obj: Boxified<T>): T {
function unboxify<T extends object>(obj: Boxified<T>): T {
let result = {} as T;
for (let k in obj) {
result[k] = unbox(obj[k]);
@@ -307,7 +307,7 @@ declare type Boxified<T> = {
declare function box<T>(x: T): Box<T>;
declare function unbox<T>(x: Box<T>): T;
declare function boxify<T>(obj: T): Boxified<T>;
declare function unboxify<T>(obj: Boxified<T>): T;
declare function unboxify<T extends object>(obj: Boxified<T>): T;
declare function assignBoxified<T>(obj: Boxified<T>, values: T): void;
declare function f1(): void;
declare function f2(): void;
@@ -75,10 +75,10 @@ function boxify<T>(obj: T): Boxified<T> {
>result : Symbol(result, Decl(isomorphicMappedTypeInference.ts, 17, 7))
}
function unboxify<T>(obj: Boxified<T>): T {
function unboxify<T extends object>(obj: Boxified<T>): T {
>unboxify : Symbol(unboxify, Decl(isomorphicMappedTypeInference.ts, 22, 1))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 24, 18))
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 24, 21))
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 24, 36))
>Boxified : Symbol(Boxified, Decl(isomorphicMappedTypeInference.ts, 2, 1))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 24, 18))
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 24, 18))
@@ -89,13 +89,13 @@ function unboxify<T>(obj: Boxified<T>): T {
for (let k in obj) {
>k : Symbol(k, Decl(isomorphicMappedTypeInference.ts, 26, 12))
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 24, 21))
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 24, 36))
result[k] = unbox(obj[k]);
>result : Symbol(result, Decl(isomorphicMappedTypeInference.ts, 25, 7))
>k : Symbol(k, Decl(isomorphicMappedTypeInference.ts, 26, 12))
>unbox : Symbol(unbox, Decl(isomorphicMappedTypeInference.ts, 10, 1))
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 24, 21))
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 24, 36))
>k : Symbol(k, Decl(isomorphicMappedTypeInference.ts, 26, 12))
}
return result;
@@ -60,8 +60,8 @@ function boxify<T>(obj: T): Boxified<T> {
>result : Boxified<T>
}
function unboxify<T>(obj: Boxified<T>): T {
>unboxify : <T>(obj: Boxified<T>) => T
function unboxify<T extends object>(obj: Boxified<T>): T {
>unboxify : <T extends object>(obj: Boxified<T>) => T
>obj : Boxified<T>
let result = {} as T;
@@ -174,7 +174,7 @@ function f2() {
let v = unboxify(b);
>v : { a: number; b: string; c: boolean; }
>unboxify(b) : { a: number; b: string; c: boolean; }
>unboxify : <T>(obj: Boxified<T>) => T
>unboxify : <T extends object>(obj: Boxified<T>) => T
>b : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
let x: number = v.a;
@@ -251,14 +251,14 @@ function f4() {
>boxify(unboxify(b)) : Boxified<{ a: number; b: string; c: boolean; }>
>boxify : <T>(obj: T) => Boxified<T>
>unboxify(b) : { a: number; b: string; c: boolean; }
>unboxify : <T>(obj: Boxified<T>) => T
>unboxify : <T extends object>(obj: Boxified<T>) => T
>b : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
b = unboxify(boxify(b));
>b = unboxify(boxify(b)) : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
>b : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
>unboxify(boxify(b)) : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
>unboxify : <T>(obj: Boxified<T>) => T
>unboxify : <T extends object>(obj: Boxified<T>) => T
>boxify(b) : Boxified<{ a: Box<number>; b: Box<string>; c: Box<boolean>; }>
>boxify : <T>(obj: T) => Boxified<T>
>b : { a: Box<number>; b: Box<string>; c: Box<boolean>; }
@@ -304,7 +304,7 @@ function f5(s: string) {
let v = unboxify(b);
>v : { a: string | number | boolean; b: string | number | boolean; c: string | number | boolean; }
>unboxify(b) : { a: string | number | boolean; b: string | number | boolean; c: string | number | boolean; }
>unboxify : <T>(obj: Boxified<T>) => T
>unboxify : <T extends object>(obj: Boxified<T>) => T
>b : { a: Box<number> | Box<string> | Box<boolean>; b: Box<number> | Box<string> | Box<boolean>; c: Box<number> | Box<string> | Box<boolean>; }
let x: string | number | boolean = v.a;
@@ -355,7 +355,7 @@ function f6(s: string) {
let v = unboxify(b);
>v : { [x: string]: any; }
>unboxify(b) : { [x: string]: any; }
>unboxify : <T>(obj: Boxified<T>) => T
>unboxify : <T extends object>(obj: Boxified<T>) => T
>b : { [x: string]: Box<number> | Box<string> | Box<boolean>; }
let x: string | number | boolean = v[s];
@@ -0,0 +1,709 @@
tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts(316,5): error TS2322: Type 'T' is not assignable to type '{}'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts(317,5): error TS2322: Type 'T[keyof T]' is not assignable to type '{}'.
Type 'T[string] | T[number] | T[symbol]' is not assignable to type '{}'.
Type 'T[string]' is not assignable to type '{}'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts(318,5): error TS2322: Type 'T[K]' is not assignable to type '{}'.
Type 'T[keyof T]' is not assignable to type '{}'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts(323,5): error TS2322: Type 'T' is not assignable to type '{} | null | undefined'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts(324,5): error TS2322: Type 'T[keyof T]' is not assignable to type '{} | null | undefined'.
Type 'T[string] | T[number] | T[symbol]' is not assignable to type '{} | null | undefined'.
Type 'T[string]' is not assignable to type '{} | null | undefined'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts(325,5): error TS2322: Type 'T[K]' is not assignable to type '{} | null | undefined'.
Type 'T[keyof T]' is not assignable to type '{} | null | undefined'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts(611,33): error TS2345: Argument of type 'T[K]' is not assignable to parameter of type '{} | null | undefined'.
Type 'T[keyof T]' is not assignable to type '{} | null | undefined'.
Type 'T[string] | T[number] | T[symbol]' is not assignable to type '{} | null | undefined'.
Type 'T[string]' is not assignable to type '{} | null | undefined'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts(619,13): error TS2322: Type 'T[keyof T]' is not assignable to type '{} | null | undefined'.
Type 'T[string] | T[number] | T[symbol]' is not assignable to type '{} | null | undefined'.
Type 'T[string]' is not assignable to type '{} | null | undefined'.
==== tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts (8 errors) ====
class Shape {
name: string;
width: number;
height: number;
visible: boolean;
}
class TaggedShape extends Shape {
tag: string;
}
class Item {
name: string;
price: number;
}
class Options {
visible: "yes" | "no";
}
type Dictionary<T> = { [x: string]: T };
type NumericallyIndexed<T> = { [x: number]: T };
const enum E { A, B, C }
type K00 = keyof any; // string
type K01 = keyof string; // "toString" | "charAt" | ...
type K02 = keyof number; // "toString" | "toFixed" | "toExponential" | ...
type K03 = keyof boolean; // "valueOf"
type K04 = keyof void; // never
type K05 = keyof undefined; // never
type K06 = keyof null; // never
type K07 = keyof never; // string | number | symbol
type K08 = keyof unknown; // never
type K10 = keyof Shape; // "name" | "width" | "height" | "visible"
type K11 = keyof Shape[]; // "length" | "toString" | ...
type K12 = keyof Dictionary<Shape>; // string
type K13 = keyof {}; // never
type K14 = keyof Object; // "constructor" | "toString" | ...
type K15 = keyof E; // "toString" | "toFixed" | "toExponential" | ...
type K16 = keyof [string, number]; // "0" | "1" | "length" | "toString" | ...
type K17 = keyof (Shape | Item); // "name"
type K18 = keyof (Shape & Item); // "name" | "width" | "height" | "visible" | "price"
type K19 = keyof NumericallyIndexed<Shape> // never
type KeyOf<T> = keyof T;
type K20 = KeyOf<Shape>; // "name" | "width" | "height" | "visible"
type K21 = KeyOf<Dictionary<Shape>>; // string
type NAME = "name";
type WIDTH_OR_HEIGHT = "width" | "height";
type Q10 = Shape["name"]; // string
type Q11 = Shape["width" | "height"]; // number
type Q12 = Shape["name" | "visible"]; // string | boolean
type Q20 = Shape[NAME]; // string
type Q21 = Shape[WIDTH_OR_HEIGHT]; // number
type Q30 = [string, number][0]; // string
type Q31 = [string, number][1]; // number
type Q32 = [string, number][number]; // string | number
type Q33 = [string, number][E.A]; // string
type Q34 = [string, number][E.B]; // number
type Q35 = [string, number]["0"]; // string
type Q36 = [string, number]["1"]; // string
type Q40 = (Shape | Options)["visible"]; // boolean | "yes" | "no"
type Q41 = (Shape & Options)["visible"]; // true & "yes" | true & "no" | false & "yes" | false & "no"
type Q50 = Dictionary<Shape>["howdy"]; // Shape
type Q51 = Dictionary<Shape>[123]; // Shape
type Q52 = Dictionary<Shape>[E.B]; // Shape
declare let cond: boolean;
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
function setProperty<T, K extends keyof T>(obj: T, key: K, value: T[K]) {
obj[key] = value;
}
function f10(shape: Shape) {
let name = getProperty(shape, "name"); // string
let widthOrHeight = getProperty(shape, cond ? "width" : "height"); // number
let nameOrVisible = getProperty(shape, cond ? "name" : "visible"); // string | boolean
setProperty(shape, "name", "rectangle");
setProperty(shape, cond ? "width" : "height", 10);
setProperty(shape, cond ? "name" : "visible", true); // Technically not safe
}
function f11(a: Shape[]) {
let len = getProperty(a, "length"); // number
setProperty(a, "length", len);
}
function f12(t: [Shape, boolean]) {
let len = getProperty(t, "length");
let s2 = getProperty(t, "0"); // Shape
let b2 = getProperty(t, "1"); // boolean
}
function f13(foo: any, bar: any) {
let x = getProperty(foo, "x"); // any
let y = getProperty(foo, "100"); // any
let z = getProperty(foo, bar); // any
}
class Component<PropType> {
props: PropType;
getProperty<K extends keyof PropType>(key: K) {
return this.props[key];
}
setProperty<K extends keyof PropType>(key: K, value: PropType[K]) {
this.props[key] = value;
}
}
function f20(component: Component<Shape>) {
let name = component.getProperty("name"); // string
let widthOrHeight = component.getProperty(cond ? "width" : "height"); // number
let nameOrVisible = component.getProperty(cond ? "name" : "visible"); // string | boolean
component.setProperty("name", "rectangle");
component.setProperty(cond ? "width" : "height", 10)
component.setProperty(cond ? "name" : "visible", true); // Technically not safe
}
function pluck<T, K extends keyof T>(array: T[], key: K) {
return array.map(x => x[key]);
}
function f30(shapes: Shape[]) {
let names = pluck(shapes, "name"); // string[]
let widths = pluck(shapes, "width"); // number[]
let nameOrVisibles = pluck(shapes, cond ? "name" : "visible"); // (string | boolean)[]
}
function f31<K extends keyof Shape>(key: K) {
const shape: Shape = { name: "foo", width: 5, height: 10, visible: true };
return shape[key]; // Shape[K]
}
function f32<K extends "width" | "height">(key: K) {
const shape: Shape = { name: "foo", width: 5, height: 10, visible: true };
return shape[key]; // Shape[K]
}
function f33<S extends Shape, K extends keyof S>(shape: S, key: K) {
let name = getProperty(shape, "name");
let prop = getProperty(shape, key);
return prop;
}
function f34(ts: TaggedShape) {
let tag1 = f33(ts, "tag");
let tag2 = getProperty(ts, "tag");
}
class C {
public x: string;
protected y: string;
private z: string;
}
// Indexed access expressions have always permitted access to private and protected members.
// For consistency we also permit such access in indexed access types.
function f40(c: C) {
type X = C["x"];
type Y = C["y"];
type Z = C["z"];
let x: X = c["x"];
let y: Y = c["y"];
let z: Z = c["z"];
}
function f50<T>(k: keyof T, s: string) {
const x1 = s as keyof T;
const x2 = k as string;
}
function f51<T, K extends keyof T>(k: K, s: string) {
const x1 = s as keyof T;
const x2 = k as string;
}
function f52<T>(obj: { [x: string]: boolean }, k: Exclude<keyof T, symbol>, s: string, n: number) {
const x1 = obj[s];
const x2 = obj[n];
const x3 = obj[k];
}
function f53<T, K extends Exclude<keyof T, symbol>>(obj: { [x: string]: boolean }, k: K, s: string, n: number) {
const x1 = obj[s];
const x2 = obj[n];
const x3 = obj[k];
}
function f54<T>(obj: T, key: keyof T) {
for (let s in obj[key]) {
}
const b = "foo" in obj[key];
}
function f55<T, K extends keyof T>(obj: T, key: K) {
for (let s in obj[key]) {
}
const b = "foo" in obj[key];
}
function f60<T>(source: T, target: T) {
for (let k in source) {
target[k] = source[k];
}
}
function f70(func: <T, U>(k1: keyof (T | U), k2: keyof (T & U)) => void) {
func<{ a: any, b: any }, { a: any, c: any }>('a', 'a');
func<{ a: any, b: any }, { a: any, c: any }>('a', 'b');
func<{ a: any, b: any }, { a: any, c: any }>('a', 'c');
}
function f71(func: <T, U>(x: T, y: U) => Partial<T & U>) {
let x = func({ a: 1, b: "hello" }, { c: true });
x.a; // number | undefined
x.b; // string | undefined
x.c; // boolean | undefined
}
function f72(func: <T, U, K extends keyof T | keyof U>(x: T, y: U, k: K) => (T & U)[K]) {
let a = func({ a: 1, b: "hello" }, { c: true }, 'a'); // number
let b = func({ a: 1, b: "hello" }, { c: true }, 'b'); // string
let c = func({ a: 1, b: "hello" }, { c: true }, 'c'); // boolean
}
function f73(func: <T, U, K extends keyof (T & U)>(x: T, y: U, k: K) => (T & U)[K]) {
let a = func({ a: 1, b: "hello" }, { c: true }, 'a'); // number
let b = func({ a: 1, b: "hello" }, { c: true }, 'b'); // string
let c = func({ a: 1, b: "hello" }, { c: true }, 'c'); // boolean
}
function f74(func: <T, U, K extends keyof (T | U)>(x: T, y: U, k: K) => (T | U)[K]) {
let a = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'a'); // number
let b = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'b'); // string | boolean
}
function f80<T extends { a: { x: any } }>(obj: T) {
let a1 = obj.a; // { x: any }
let a2 = obj['a']; // { x: any }
let a3 = obj['a'] as T['a']; // T["a"]
let x1 = obj.a.x; // any
let x2 = obj['a']['x']; // any
let x3 = obj['a']['x'] as T['a']['x']; // T["a"]["x"]
}
function f81<T extends { a: { x: any } }>(obj: T) {
return obj['a']['x'] as T['a']['x'];
}
function f82() {
let x1 = f81({ a: { x: "hello" } }); // string
let x2 = f81({ a: { x: 42 } }); // number
}
function f83<T extends { [x: string]: { x: any } }, K extends keyof T>(obj: T, key: K) {
return obj[key]['x'] as T[K]['x'];
}
function f84() {
let x1 = f83({ foo: { x: "hello" } }, "foo"); // string
let x2 = f83({ bar: { x: 42 } }, "bar"); // number
}
class C1 {
x: number;
get<K extends keyof this>(key: K) {
return this[key];
}
set<K extends keyof this>(key: K, value: this[K]) {
this[key] = value;
}
foo() {
let x1 = this.x; // number
let x2 = this["x"]; // number
let x3 = this.get("x"); // this["x"]
let x4 = getProperty(this, "x"); // this["x"]
this.x = 42;
this["x"] = 42;
this.set("x", 42);
setProperty(this, "x", 42);
}
}
type S2 = {
a: string;
b: string;
};
function f90<T extends S2, K extends keyof S2>(x1: S2[keyof S2], x2: T[keyof S2], x3: S2[K]) {
x1 = x2;
x1 = x3;
x2 = x1;
x2 = x3;
x3 = x1;
x3 = x2;
x1.length;
x2.length;
x3.length;
}
function f91<T, K extends keyof T>(x: T, y: T[keyof T], z: T[K]) {
let a: {};
a = x;
~
!!! error TS2322: Type 'T' is not assignable to type '{}'.
!!! related TS2208 tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts:314:14: This type parameter probably needs an `extends object` constraint.
a = y;
~
!!! error TS2322: Type 'T[keyof T]' is not assignable to type '{}'.
!!! error TS2322: Type 'T[string] | T[number] | T[symbol]' is not assignable to type '{}'.
!!! error TS2322: Type 'T[string]' is not assignable to type '{}'.
a = z;
~
!!! error TS2322: Type 'T[K]' is not assignable to type '{}'.
!!! error TS2322: Type 'T[keyof T]' is not assignable to type '{}'.
}
function f92<T, K extends keyof T>(x: T, y: T[keyof T], z: T[K]) {
let a: {} | null | undefined;
a = x;
~
!!! error TS2322: Type 'T' is not assignable to type '{} | null | undefined'.
!!! related TS2208 tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts:321:14: This type parameter probably needs an `extends object` constraint.
a = y;
~
!!! error TS2322: Type 'T[keyof T]' is not assignable to type '{} | null | undefined'.
!!! error TS2322: Type 'T[string] | T[number] | T[symbol]' is not assignable to type '{} | null | undefined'.
!!! error TS2322: Type 'T[string]' is not assignable to type '{} | null | undefined'.
a = z;
~
!!! error TS2322: Type 'T[K]' is not assignable to type '{} | null | undefined'.
!!! error TS2322: Type 'T[keyof T]' is not assignable to type '{} | null | undefined'.
}
// Repros from #12011
class Base {
get<K extends keyof this>(prop: K) {
return this[prop];
}
set<K extends keyof this>(prop: K, value: this[K]) {
this[prop] = value;
}
}
class Person extends Base {
parts: number;
constructor(parts: number) {
super();
this.set("parts", parts);
}
getParts() {
return this.get("parts")
}
}
class OtherPerson {
parts: number;
constructor(parts: number) {
setProperty(this, "parts", parts);
}
getParts() {
return getProperty(this, "parts")
}
}
// Modified repro from #12544
function path<T, K1 extends keyof T>(obj: T, key1: K1): T[K1];
function path<T, K1 extends keyof T, K2 extends keyof T[K1]>(obj: T, key1: K1, key2: K2): T[K1][K2];
function path<T, K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2]>(obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3];
function path(obj: any, ...keys: (string | number)[]): any;
function path(obj: any, ...keys: (string | number)[]): any {
let result = obj;
for (let k of keys) {
result = result[k];
}
return result;
}
type Thing = {
a: { x: number, y: string },
b: boolean
};
function f1(thing: Thing) {
let x1 = path(thing, 'a'); // { x: number, y: string }
let x2 = path(thing, 'a', 'y'); // string
let x3 = path(thing, 'b'); // boolean
let x4 = path(thing, ...['a', 'x']); // any
}
// Repro from comment in #12114
const assignTo2 = <T, K1 extends keyof T, K2 extends keyof T[K1]>(object: T, key1: K1, key2: K2) =>
(value: T[K1][K2]) => object[key1][key2] = value;
// Modified repro from #12573
declare function one<T>(handler: (t: T) => void): T
var empty = one(() => {}) // inferred as {}, expected
type Handlers<T> = { [K in keyof T]: (t: T[K]) => void }
declare function on<T>(handlerHash: Handlers<T>): T
var hashOfEmpty1 = on({ test: () => {} }); // {}
var hashOfEmpty2 = on({ test: (x: boolean) => {} }); // { test: boolean }
// Repro from #12624
interface Options1<Data, Computed> {
data?: Data
computed?: Computed;
}
declare class Component1<Data, Computed> {
constructor(options: Options1<Data, Computed>);
get<K extends keyof (Data & Computed)>(key: K): (Data & Computed)[K];
}
let c1 = new Component1({
data: {
hello: ""
}
});
c1.get("hello");
// Repro from #12625
interface Options2<Data, Computed> {
data?: Data
computed?: Computed;
}
declare class Component2<Data, Computed> {
constructor(options: Options2<Data, Computed>);
get<K extends keyof Data | keyof Computed>(key: K): (Data & Computed)[K];
}
// Repro from #12641
interface R {
p: number;
}
function f<K extends keyof R>(p: K) {
let a: any;
a[p].add; // any
}
// Repro from #12651
type MethodDescriptor = {
name: string;
args: any[];
returnValue: any;
}
declare function dispatchMethod<M extends MethodDescriptor>(name: M['name'], args: M['args']): M['returnValue'];
type SomeMethodDescriptor = {
name: "someMethod";
args: [string, number];
returnValue: string[];
}
let result = dispatchMethod<SomeMethodDescriptor>("someMethod", ["hello", 35]);
// Repro from #13073
type KeyTypes = "a" | "b"
let MyThingy: { [key in KeyTypes]: string[] };
function addToMyThingy<S extends KeyTypes>(key: S) {
MyThingy[key].push("a");
}
// Repro from #13102
type Handler<T> = {
onChange: (name: keyof T) => void;
};
function onChangeGenericFunction<T>(handler: Handler<T & {preset: number}>) {
handler.onChange('preset')
}
// Repro from #13285
function updateIds<T extends Record<K, string>, K extends string>(
obj: T,
idFields: K[],
idMapping: Partial<Record<T[K], T[K]>>
): Record<K, string> {
for (const idField of idFields) {
const newId: T[K] | undefined = idMapping[obj[idField]];
if (newId) {
obj[idField] = newId;
}
}
return obj;
}
// Repro from #13285
function updateIds2<T extends { [x: string]: string }, K extends keyof T>(
obj: T,
key: K,
stringMap: { [oldId: string]: string }
) {
var x = obj[key];
stringMap[x]; // Should be OK.
}
// Repro from #13514
declare function head<T extends Array<any>>(list: T): T[0];
// Repro from #13604
class A<T> {
props: T & { foo: string };
}
class B extends A<{ x: number}> {
f(p: this["props"]) {
p.x;
}
}
// Repro from #13749
class Form<T> {
private childFormFactories: {[K in keyof T]: (v: T[K]) => Form<T[K]>}
public set<K extends keyof T>(prop: K, value: T[K]) {
this.childFormFactories[prop](value)
}
}
// Repro from #13787
class SampleClass<P> {
public props: Readonly<P>;
constructor(props: P) {
this.props = Object.freeze(props);
}
}
interface Foo {
foo: string;
}
declare function merge<T, U>(obj1: T, obj2: U): T & U;
class AnotherSampleClass<T> extends SampleClass<T & Foo> {
constructor(props: T) {
const foo: Foo = { foo: "bar" };
super(merge(props, foo));
}
public brokenMethod() {
this.props.foo.concat;
}
}
new AnotherSampleClass({});
// Positive repro from #17166
function f3<T, K extends Extract<keyof T, string>>(t: T, k: K, tk: T[K]): void {
for (let key in t) {
key = k // ok, K ==> keyof T
t[key] = tk; // ok, T[K] ==> T[keyof T]
}
}
// # 21185
type Predicates<TaggedRecord> = {
[T in keyof TaggedRecord]: (variant: TaggedRecord[keyof TaggedRecord]) => variant is TaggedRecord[T]
}
// Repros from #23592
type Example<T extends { [K in keyof T]: { prop: any } }> = { [K in keyof T]: T[K]["prop"] };
type Result = Example<{ a: { prop: string }; b: { prop: number } }>;
type Helper2<T> = { [K in keyof T]: Extract<T[K], { prop: any }> };
type Example2<T> = { [K in keyof Helper2<T>]: Helper2<T>[K]["prop"] };
type Result2 = Example2<{ 1: { prop: string }; 2: { prop: number } }>;
// Repro from #23618
type DBBoolTable<K extends string> = { [k in K]: 0 | 1 }
enum Flag {
FLAG_1 = "flag_1",
FLAG_2 = "flag_2"
}
type SimpleDBRecord<Flag extends string> = { staticField: number } & DBBoolTable<Flag>
function getFlagsFromSimpleRecord<Flag extends string>(record: SimpleDBRecord<Flag>, flags: Flag[]) {
return record[flags[0]];
}
type DynamicDBRecord<Flag extends string> = ({ dynamicField: number } | { dynamicField: string }) & DBBoolTable<Flag>
function getFlagsFromDynamicRecord<Flag extends string>(record: DynamicDBRecord<Flag>, flags: Flag[]) {
return record[flags[0]];
}
// Repro from #21368
interface I {
foo: string;
}
declare function take<T>(p: T): void;
function fn<T extends I, K extends keyof T>(o: T, k: K) {
take<{} | null | undefined>(o[k]);
~~~~
!!! error TS2345: Argument of type 'T[K]' is not assignable to parameter of type '{} | null | undefined'.
!!! error TS2345: Type 'T[keyof T]' is not assignable to type '{} | null | undefined'.
!!! error TS2345: Type 'T[string] | T[number] | T[symbol]' is not assignable to type '{} | null | undefined'.
!!! error TS2345: Type 'T[string]' is not assignable to type '{} | null | undefined'.
take<any>(o[k]);
}
// Repro from #23133
class Unbounded<T> {
foo(x: T[keyof T]) {
let y: {} | undefined | null = x;
~
!!! error TS2322: Type 'T[keyof T]' is not assignable to type '{} | null | undefined'.
!!! error TS2322: Type 'T[string] | T[number] | T[symbol]' is not assignable to type '{} | null | undefined'.
!!! error TS2322: Type 'T[string]' is not assignable to type '{} | null | undefined'.
}
}
// Repro from #23940
interface I7 {
x: any;
}
type Foo7<T extends number> = T;
declare function f7<K extends keyof I7>(type: K): Foo7<I7[K]>;
// Repro from #21770
type Dict<T extends string> = { [key in T]: number };
type DictDict<V extends string, T extends string> = { [key in V]: Dict<T> };
function ff1<V extends string, T extends string>(dd: DictDict<V, T>, k1: V, k2: T): number {
return dd[k1][k2];
}
function ff2<V extends string, T extends string>(dd: DictDict<V, T>, k1: V, k2: T): number {
const d: Dict<T> = dd[k1];
return d[k2];
}
// Repro from #26409
const cf1 = <T extends { [P in K]: string; } & { cool: string; }, K extends keyof T>(t: T, k: K) =>
{
const s: string = t[k];
t.cool;
};
const cf2 = <T extends { [P in K | "cool"]: string; }, K extends keyof T>(t: T, k: K) =>
{
const s: string = t[k];
t.cool;
};
@@ -0,0 +1,54 @@
tests/cases/conformance/types/mapped/mappedTypesAndObjects.ts(25,11): error TS2430: Interface 'E1<T>' incorrectly extends interface 'Base'.
Types of property 'foo' are incompatible.
Type 'T' is not assignable to type '{ [key: string]: any; }'.
==== tests/cases/conformance/types/mapped/mappedTypesAndObjects.ts (1 errors) ====
function f1<T>(x: Partial<T>, y: Readonly<T>) {
let obj: {};
obj = x;
obj = y;
}
function f2<T>(x: Partial<T>, y: Readonly<T>) {
let obj: { [x: string]: any };
obj = x;
obj = y;
}
function f3<T>(x: Partial<T>) {
x = {};
}
// Repro from #12900
interface Base {
foo: { [key: string]: any };
bar: any;
baz: any;
}
interface E1<T> extends Base {
~~
!!! error TS2430: Interface 'E1<T>' incorrectly extends interface 'Base'.
!!! error TS2430: Types of property 'foo' are incompatible.
!!! error TS2430: Type 'T' is not assignable to type '{ [key: string]: any; }'.
!!! related TS2208 tests/cases/conformance/types/mapped/mappedTypesAndObjects.ts:25:14: This type parameter probably needs an `extends object` constraint.
foo: T;
}
interface Something { name: string, value: string };
interface E2 extends Base {
foo: Partial<Something>; // or other mapped type
}
interface E3<T> extends Base {
foo: Partial<T>; // or other mapped type
}
// Repro from #13747
class Form<T> {
private values: {[P in keyof T]?: T[P]} = {}
}
@@ -5,9 +5,17 @@ tests/cases/compiler/tsxNotUsingApparentTypeOfSFC.tsx(15,14): error TS2769: No o
Type '{}' is not assignable to type 'Readonly<P>'.
Overload 2 of 2, '(props: P, context?: any): MyComponent', gave the following error.
Type '{}' is not assignable to type 'Readonly<P>'.
tests/cases/compiler/tsxNotUsingApparentTypeOfSFC.tsx(17,14): error TS2322: Type 'P' is not assignable to type 'IntrinsicAttributes & P'.
Type 'P' is not assignable to type 'IntrinsicAttributes'.
tests/cases/compiler/tsxNotUsingApparentTypeOfSFC.tsx(18,14): error TS2769: No overload matches this call.
Overload 1 of 2, '(props: Readonly<P>): MyComponent', gave the following error.
Type 'P' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<MyComponent> & Readonly<{ children?: ReactNode; }> & Readonly<P>'.
Type 'P' is not assignable to type 'IntrinsicAttributes'.
Overload 2 of 2, '(props: P, context?: any): MyComponent', gave the following error.
Type 'P' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<MyComponent> & Readonly<{ children?: ReactNode; }> & Readonly<P>'.
==== tests/cases/compiler/tsxNotUsingApparentTypeOfSFC.tsx (2 errors) ====
==== tests/cases/compiler/tsxNotUsingApparentTypeOfSFC.tsx (4 errors) ====
/// <reference path="/.lib/react16.d.ts" />
import React from 'react';
@@ -34,5 +42,17 @@ tests/cases/compiler/tsxNotUsingApparentTypeOfSFC.tsx(15,14): error TS2769: No o
!!! error TS2769: Type '{}' is not assignable to type 'Readonly<P>'.
let z = <MySFC {...wrappedProps} /> // should work
~~~~~
!!! error TS2322: Type 'P' is not assignable to type 'IntrinsicAttributes & P'.
!!! error TS2322: Type 'P' is not assignable to type 'IntrinsicAttributes'.
!!! related TS2208 tests/cases/compiler/tsxNotUsingApparentTypeOfSFC.tsx:5:15: This type parameter probably needs an `extends object` constraint.
let q = <MyComponent {...wrappedProps} /> // should work
~~~~~~~~~~~
!!! error TS2769: No overload matches this call.
!!! error TS2769: Overload 1 of 2, '(props: Readonly<P>): MyComponent', gave the following error.
!!! error TS2769: Type 'P' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<MyComponent> & Readonly<{ children?: ReactNode; }> & Readonly<P>'.
!!! error TS2769: Type 'P' is not assignable to type 'IntrinsicAttributes'.
!!! error TS2769: Overload 2 of 2, '(props: P, context?: any): MyComponent', gave the following error.
!!! error TS2769: Type 'P' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<MyComponent> & Readonly<{ children?: ReactNode; }> & Readonly<P>'.
!!! related TS2208 tests/cases/compiler/tsxNotUsingApparentTypeOfSFC.tsx:5:15: This type parameter probably needs an `extends object` constraint.
}
@@ -25,13 +25,14 @@ tests/cases/conformance/types/unknown/unknownType1.ts(144,29): error TS2698: Spr
tests/cases/conformance/types/unknown/unknownType1.ts(150,17): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
tests/cases/conformance/types/unknown/unknownType1.ts(156,14): error TS2700: Rest types may only be created from object types.
tests/cases/conformance/types/unknown/unknownType1.ts(162,5): error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor.
tests/cases/conformance/types/unknown/unknownType1.ts(170,9): error TS2322: Type 'T' is not assignable to type '{}'.
tests/cases/conformance/types/unknown/unknownType1.ts(171,9): error TS2322: Type 'U' is not assignable to type '{}'.
Type 'unknown' is not assignable to type '{}'.
tests/cases/conformance/types/unknown/unknownType1.ts(181,5): error TS2322: Type 'T' is not assignable to type '{}'.
Type 'unknown' is not assignable to type '{}'.
==== tests/cases/conformance/types/unknown/unknownType1.ts (27 errors) ====
==== tests/cases/conformance/types/unknown/unknownType1.ts (28 errors) ====
// In an intersection everything absorbs unknown
type T00 = unknown & null; // null
@@ -254,6 +255,9 @@ tests/cases/conformance/types/unknown/unknownType1.ts(181,5): error TS2322: Type
function f30<T, U extends unknown>(t: T, u: U) {
let x: {} = t;
~
!!! error TS2322: Type 'T' is not assignable to type '{}'.
!!! related TS2208 tests/cases/conformance/types/unknown/unknownType1.ts:169:14: This type parameter probably needs an `extends object` constraint.
let y: {} = u;
~
!!! error TS2322: Type 'U' is not assignable to type '{}'.
@@ -26,7 +26,7 @@ function boxify<T>(obj: T): Boxified<T> {
return result;
}
function unboxify<T>(obj: Boxified<T>): T {
function unboxify<T extends object>(obj: Boxified<T>): T {
let result = {} as T;
for (let k in obj) {
result[k] = unbox(obj[k]);