mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-19 10:41:56 -05:00
Slightly more conservative check in isConstraintPosition function (#44621)
* Slightly more conservative check in isConstraintPosition function * Update comment * Add tests
This commit is contained in:
@@ -24140,21 +24140,25 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function isConstraintPosition(node: Node) {
|
||||
function isConstraintPosition(type: Type, node: Node) {
|
||||
const parent = node.parent;
|
||||
// In an element access obj[x], we consider obj to be in a constraint position only when x is not
|
||||
// of a generic type. This is because when both obj and x are of generic types T and K, we want
|
||||
// the resulting type to be T[K].
|
||||
// In an element access obj[x], we consider obj to be in a constraint position, except when obj is of
|
||||
// a generic type without a nullable constraint and x is a generic type. This is because when both obj
|
||||
// and x are of generic types T and K, we want the resulting type to be T[K].
|
||||
return parent.kind === SyntaxKind.PropertyAccessExpression ||
|
||||
parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === node ||
|
||||
parent.kind === SyntaxKind.ElementAccessExpression && (parent as ElementAccessExpression).expression === node &&
|
||||
!isGenericIndexType(getTypeOfExpression((parent as ElementAccessExpression).argumentExpression));
|
||||
!(isGenericTypeWithoutNullableConstraint(type) && isGenericIndexType(getTypeOfExpression((parent as ElementAccessExpression).argumentExpression)));
|
||||
}
|
||||
|
||||
function isGenericTypeWithUnionConstraint(type: Type) {
|
||||
return !!(type.flags & TypeFlags.Instantiable && getBaseConstraintOrType(type).flags & (TypeFlags.Nullable | TypeFlags.Union));
|
||||
}
|
||||
|
||||
function isGenericTypeWithoutNullableConstraint(type: Type) {
|
||||
return !!(type.flags & TypeFlags.Instantiable && !maybeTypeOfKind(getBaseConstraintOrType(type), TypeFlags.Nullable));
|
||||
}
|
||||
|
||||
function containsGenericType(type: Type): boolean {
|
||||
return !!(type.flags & TypeFlags.Instantiable || type.flags & TypeFlags.UnionOrIntersection && some((type as UnionOrIntersectionType).types, containsGenericType));
|
||||
}
|
||||
@@ -24178,7 +24182,7 @@ namespace ts {
|
||||
// 'string | undefined' to give control flow analysis the opportunity to narrow to type 'string'.
|
||||
const substituteConstraints = !(checkMode && checkMode & CheckMode.Inferential) &&
|
||||
someType(type, isGenericTypeWithUnionConstraint) &&
|
||||
(isConstraintPosition(reference) || hasNonBindingPatternContextualTypeWithNoGenericTypes(reference));
|
||||
(isConstraintPosition(type, reference) || hasNonBindingPatternContextualTypeWithNoGenericTypes(reference));
|
||||
return substituteConstraints ? mapType(type, t => t.flags & TypeFlags.Instantiable ? getBaseConstraintOrType(t) : t) : type;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,12 @@ tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts(81,11): error TS2
|
||||
tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts(90,44): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
|
||||
tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts(91,11): error TS2339: Property 'foo' does not exist on type 'MyUnion'.
|
||||
Property 'foo' does not exist on type 'AA'.
|
||||
tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts(156,16): error TS2532: Object is possibly 'undefined'.
|
||||
tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts(167,9): error TS2532: Object is possibly 'undefined'.
|
||||
tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts(168,9): error TS2532: Object is possibly 'undefined'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts (5 errors) ====
|
||||
==== tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts (8 errors) ====
|
||||
function f1<T extends string | undefined>(x: T, y: { a: T }, z: [T]): string {
|
||||
if (x) {
|
||||
x;
|
||||
@@ -159,4 +162,46 @@ tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts(91,11): error TS2
|
||||
emittingObject.off(eventName, 0);
|
||||
emittingObject.off(eventName as typeof eventName, 0);
|
||||
}
|
||||
|
||||
// In an element access obj[x], we consider obj to be in a constraint position, except when obj is of
|
||||
// a generic type without a nullable constraint and x is a generic type. This is because when both obj
|
||||
// and x are of generic types T and K, we want the resulting type to be T[K].
|
||||
|
||||
function fx1<T, K extends keyof T>(obj: T, key: K) {
|
||||
const x1 = obj[key];
|
||||
const x2 = obj && obj[key];
|
||||
}
|
||||
|
||||
function fx2<T extends Record<keyof T, string>, K extends keyof T>(obj: T, key: K) {
|
||||
const x1 = obj[key];
|
||||
const x2 = obj && obj[key];
|
||||
}
|
||||
|
||||
function fx3<T extends Record<keyof T, string> | undefined, K extends keyof T>(obj: T, key: K) {
|
||||
const x1 = obj[key]; // Error
|
||||
~~~
|
||||
!!! error TS2532: Object is possibly 'undefined'.
|
||||
const x2 = obj && obj[key];
|
||||
}
|
||||
|
||||
// Repro from #44166
|
||||
|
||||
class TableBaseEnum<
|
||||
PublicSpec extends Record<keyof InternalSpec, any>,
|
||||
InternalSpec extends Record<keyof PublicSpec, any> | undefined = undefined> {
|
||||
m() {
|
||||
let iSpec = null! as InternalSpec;
|
||||
iSpec[null! as keyof InternalSpec]; // Error, object possibly undefined
|
||||
~~~~~
|
||||
!!! error TS2532: Object is possibly 'undefined'.
|
||||
iSpec[null! as keyof PublicSpec]; // Error, object possibly undefined
|
||||
~~~~~
|
||||
!!! error TS2532: Object is possibly 'undefined'.
|
||||
if (iSpec === undefined) {
|
||||
return;
|
||||
}
|
||||
iSpec[null! as keyof InternalSpec];
|
||||
iSpec[null! as keyof PublicSpec];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +138,42 @@ function once<ET, T extends EventEmitter<ET>>(emittingObject: T, eventName: keyo
|
||||
emittingObject.off(eventName, 0);
|
||||
emittingObject.off(eventName as typeof eventName, 0);
|
||||
}
|
||||
|
||||
// In an element access obj[x], we consider obj to be in a constraint position, except when obj is of
|
||||
// a generic type without a nullable constraint and x is a generic type. This is because when both obj
|
||||
// and x are of generic types T and K, we want the resulting type to be T[K].
|
||||
|
||||
function fx1<T, K extends keyof T>(obj: T, key: K) {
|
||||
const x1 = obj[key];
|
||||
const x2 = obj && obj[key];
|
||||
}
|
||||
|
||||
function fx2<T extends Record<keyof T, string>, K extends keyof T>(obj: T, key: K) {
|
||||
const x1 = obj[key];
|
||||
const x2 = obj && obj[key];
|
||||
}
|
||||
|
||||
function fx3<T extends Record<keyof T, string> | undefined, K extends keyof T>(obj: T, key: K) {
|
||||
const x1 = obj[key]; // Error
|
||||
const x2 = obj && obj[key];
|
||||
}
|
||||
|
||||
// Repro from #44166
|
||||
|
||||
class TableBaseEnum<
|
||||
PublicSpec extends Record<keyof InternalSpec, any>,
|
||||
InternalSpec extends Record<keyof PublicSpec, any> | undefined = undefined> {
|
||||
m() {
|
||||
let iSpec = null! as InternalSpec;
|
||||
iSpec[null! as keyof InternalSpec]; // Error, object possibly undefined
|
||||
iSpec[null! as keyof PublicSpec]; // Error, object possibly undefined
|
||||
if (iSpec === undefined) {
|
||||
return;
|
||||
}
|
||||
iSpec[null! as keyof InternalSpec];
|
||||
iSpec[null! as keyof PublicSpec];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//// [controlFlowGenericTypes.js]
|
||||
@@ -246,3 +282,34 @@ function once(emittingObject, eventName) {
|
||||
emittingObject.off(eventName, 0);
|
||||
emittingObject.off(eventName, 0);
|
||||
}
|
||||
// In an element access obj[x], we consider obj to be in a constraint position, except when obj is of
|
||||
// a generic type without a nullable constraint and x is a generic type. This is because when both obj
|
||||
// and x are of generic types T and K, we want the resulting type to be T[K].
|
||||
function fx1(obj, key) {
|
||||
var x1 = obj[key];
|
||||
var x2 = obj && obj[key];
|
||||
}
|
||||
function fx2(obj, key) {
|
||||
var x1 = obj[key];
|
||||
var x2 = obj && obj[key];
|
||||
}
|
||||
function fx3(obj, key) {
|
||||
var x1 = obj[key]; // Error
|
||||
var x2 = obj && obj[key];
|
||||
}
|
||||
// Repro from #44166
|
||||
var TableBaseEnum = /** @class */ (function () {
|
||||
function TableBaseEnum() {
|
||||
}
|
||||
TableBaseEnum.prototype.m = function () {
|
||||
var iSpec = null;
|
||||
iSpec[null]; // Error, object possibly undefined
|
||||
iSpec[null]; // Error, object possibly undefined
|
||||
if (iSpec === undefined) {
|
||||
return;
|
||||
}
|
||||
iSpec[null];
|
||||
iSpec[null];
|
||||
};
|
||||
return TableBaseEnum;
|
||||
}());
|
||||
|
||||
@@ -405,3 +405,123 @@ function once<ET, T extends EventEmitter<ET>>(emittingObject: T, eventName: keyo
|
||||
>eventName : Symbol(eventName, Decl(controlFlowGenericTypes.ts, 135, 64))
|
||||
}
|
||||
|
||||
// In an element access obj[x], we consider obj to be in a constraint position, except when obj is of
|
||||
// a generic type without a nullable constraint and x is a generic type. This is because when both obj
|
||||
// and x are of generic types T and K, we want the resulting type to be T[K].
|
||||
|
||||
function fx1<T, K extends keyof T>(obj: T, key: K) {
|
||||
>fx1 : Symbol(fx1, Decl(controlFlowGenericTypes.ts, 138, 1))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 144, 13))
|
||||
>K : Symbol(K, Decl(controlFlowGenericTypes.ts, 144, 15))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 144, 13))
|
||||
>obj : Symbol(obj, Decl(controlFlowGenericTypes.ts, 144, 35))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 144, 13))
|
||||
>key : Symbol(key, Decl(controlFlowGenericTypes.ts, 144, 42))
|
||||
>K : Symbol(K, Decl(controlFlowGenericTypes.ts, 144, 15))
|
||||
|
||||
const x1 = obj[key];
|
||||
>x1 : Symbol(x1, Decl(controlFlowGenericTypes.ts, 145, 9))
|
||||
>obj : Symbol(obj, Decl(controlFlowGenericTypes.ts, 144, 35))
|
||||
>key : Symbol(key, Decl(controlFlowGenericTypes.ts, 144, 42))
|
||||
|
||||
const x2 = obj && obj[key];
|
||||
>x2 : Symbol(x2, Decl(controlFlowGenericTypes.ts, 146, 9))
|
||||
>obj : Symbol(obj, Decl(controlFlowGenericTypes.ts, 144, 35))
|
||||
>obj : Symbol(obj, Decl(controlFlowGenericTypes.ts, 144, 35))
|
||||
>key : Symbol(key, Decl(controlFlowGenericTypes.ts, 144, 42))
|
||||
}
|
||||
|
||||
function fx2<T extends Record<keyof T, string>, K extends keyof T>(obj: T, key: K) {
|
||||
>fx2 : Symbol(fx2, Decl(controlFlowGenericTypes.ts, 147, 1))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 149, 13))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 149, 13))
|
||||
>K : Symbol(K, Decl(controlFlowGenericTypes.ts, 149, 47))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 149, 13))
|
||||
>obj : Symbol(obj, Decl(controlFlowGenericTypes.ts, 149, 67))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 149, 13))
|
||||
>key : Symbol(key, Decl(controlFlowGenericTypes.ts, 149, 74))
|
||||
>K : Symbol(K, Decl(controlFlowGenericTypes.ts, 149, 47))
|
||||
|
||||
const x1 = obj[key];
|
||||
>x1 : Symbol(x1, Decl(controlFlowGenericTypes.ts, 150, 9))
|
||||
>obj : Symbol(obj, Decl(controlFlowGenericTypes.ts, 149, 67))
|
||||
>key : Symbol(key, Decl(controlFlowGenericTypes.ts, 149, 74))
|
||||
|
||||
const x2 = obj && obj[key];
|
||||
>x2 : Symbol(x2, Decl(controlFlowGenericTypes.ts, 151, 9))
|
||||
>obj : Symbol(obj, Decl(controlFlowGenericTypes.ts, 149, 67))
|
||||
>obj : Symbol(obj, Decl(controlFlowGenericTypes.ts, 149, 67))
|
||||
>key : Symbol(key, Decl(controlFlowGenericTypes.ts, 149, 74))
|
||||
}
|
||||
|
||||
function fx3<T extends Record<keyof T, string> | undefined, K extends keyof T>(obj: T, key: K) {
|
||||
>fx3 : Symbol(fx3, Decl(controlFlowGenericTypes.ts, 152, 1))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 154, 13))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 154, 13))
|
||||
>K : Symbol(K, Decl(controlFlowGenericTypes.ts, 154, 59))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 154, 13))
|
||||
>obj : Symbol(obj, Decl(controlFlowGenericTypes.ts, 154, 79))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 154, 13))
|
||||
>key : Symbol(key, Decl(controlFlowGenericTypes.ts, 154, 86))
|
||||
>K : Symbol(K, Decl(controlFlowGenericTypes.ts, 154, 59))
|
||||
|
||||
const x1 = obj[key]; // Error
|
||||
>x1 : Symbol(x1, Decl(controlFlowGenericTypes.ts, 155, 9))
|
||||
>obj : Symbol(obj, Decl(controlFlowGenericTypes.ts, 154, 79))
|
||||
>key : Symbol(key, Decl(controlFlowGenericTypes.ts, 154, 86))
|
||||
|
||||
const x2 = obj && obj[key];
|
||||
>x2 : Symbol(x2, Decl(controlFlowGenericTypes.ts, 156, 9))
|
||||
>obj : Symbol(obj, Decl(controlFlowGenericTypes.ts, 154, 79))
|
||||
>obj : Symbol(obj, Decl(controlFlowGenericTypes.ts, 154, 79))
|
||||
>key : Symbol(key, Decl(controlFlowGenericTypes.ts, 154, 86))
|
||||
}
|
||||
|
||||
// Repro from #44166
|
||||
|
||||
class TableBaseEnum<
|
||||
>TableBaseEnum : Symbol(TableBaseEnum, Decl(controlFlowGenericTypes.ts, 157, 1))
|
||||
|
||||
PublicSpec extends Record<keyof InternalSpec, any>,
|
||||
>PublicSpec : Symbol(PublicSpec, Decl(controlFlowGenericTypes.ts, 161, 20))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
>InternalSpec : Symbol(InternalSpec, Decl(controlFlowGenericTypes.ts, 162, 55))
|
||||
|
||||
InternalSpec extends Record<keyof PublicSpec, any> | undefined = undefined> {
|
||||
>InternalSpec : Symbol(InternalSpec, Decl(controlFlowGenericTypes.ts, 162, 55))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
>PublicSpec : Symbol(PublicSpec, Decl(controlFlowGenericTypes.ts, 161, 20))
|
||||
|
||||
m() {
|
||||
>m : Symbol(TableBaseEnum.m, Decl(controlFlowGenericTypes.ts, 163, 82))
|
||||
|
||||
let iSpec = null! as InternalSpec;
|
||||
>iSpec : Symbol(iSpec, Decl(controlFlowGenericTypes.ts, 165, 11))
|
||||
>InternalSpec : Symbol(InternalSpec, Decl(controlFlowGenericTypes.ts, 162, 55))
|
||||
|
||||
iSpec[null! as keyof InternalSpec]; // Error, object possibly undefined
|
||||
>iSpec : Symbol(iSpec, Decl(controlFlowGenericTypes.ts, 165, 11))
|
||||
>InternalSpec : Symbol(InternalSpec, Decl(controlFlowGenericTypes.ts, 162, 55))
|
||||
|
||||
iSpec[null! as keyof PublicSpec]; // Error, object possibly undefined
|
||||
>iSpec : Symbol(iSpec, Decl(controlFlowGenericTypes.ts, 165, 11))
|
||||
>PublicSpec : Symbol(PublicSpec, Decl(controlFlowGenericTypes.ts, 161, 20))
|
||||
|
||||
if (iSpec === undefined) {
|
||||
>iSpec : Symbol(iSpec, Decl(controlFlowGenericTypes.ts, 165, 11))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
return;
|
||||
}
|
||||
iSpec[null! as keyof InternalSpec];
|
||||
>iSpec : Symbol(iSpec, Decl(controlFlowGenericTypes.ts, 165, 11))
|
||||
>InternalSpec : Symbol(InternalSpec, Decl(controlFlowGenericTypes.ts, 162, 55))
|
||||
|
||||
iSpec[null! as keyof PublicSpec];
|
||||
>iSpec : Symbol(iSpec, Decl(controlFlowGenericTypes.ts, 165, 11))
|
||||
>PublicSpec : Symbol(PublicSpec, Decl(controlFlowGenericTypes.ts, 161, 20))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -388,3 +388,120 @@ function once<ET, T extends EventEmitter<ET>>(emittingObject: T, eventName: keyo
|
||||
>0 : 0
|
||||
}
|
||||
|
||||
// In an element access obj[x], we consider obj to be in a constraint position, except when obj is of
|
||||
// a generic type without a nullable constraint and x is a generic type. This is because when both obj
|
||||
// and x are of generic types T and K, we want the resulting type to be T[K].
|
||||
|
||||
function fx1<T, K extends keyof T>(obj: T, key: K) {
|
||||
>fx1 : <T, K extends keyof T>(obj: T, key: K) => void
|
||||
>obj : T
|
||||
>key : K
|
||||
|
||||
const x1 = obj[key];
|
||||
>x1 : T[K]
|
||||
>obj[key] : T[K]
|
||||
>obj : T
|
||||
>key : K
|
||||
|
||||
const x2 = obj && obj[key];
|
||||
>x2 : T[K]
|
||||
>obj && obj[key] : T[K]
|
||||
>obj : T
|
||||
>obj[key] : T[K]
|
||||
>obj : T
|
||||
>key : K
|
||||
}
|
||||
|
||||
function fx2<T extends Record<keyof T, string>, K extends keyof T>(obj: T, key: K) {
|
||||
>fx2 : <T extends Record<keyof T, string>, K extends keyof T>(obj: T, key: K) => void
|
||||
>obj : T
|
||||
>key : K
|
||||
|
||||
const x1 = obj[key];
|
||||
>x1 : T[K]
|
||||
>obj[key] : T[K]
|
||||
>obj : T
|
||||
>key : K
|
||||
|
||||
const x2 = obj && obj[key];
|
||||
>x2 : T[K]
|
||||
>obj && obj[key] : T[K]
|
||||
>obj : T
|
||||
>obj[key] : T[K]
|
||||
>obj : T
|
||||
>key : K
|
||||
}
|
||||
|
||||
function fx3<T extends Record<keyof T, string> | undefined, K extends keyof T>(obj: T, key: K) {
|
||||
>fx3 : <T extends Record<keyof T, string> | undefined, K extends keyof T>(obj: T, key: K) => void
|
||||
>obj : T
|
||||
>key : K
|
||||
|
||||
const x1 = obj[key]; // Error
|
||||
>x1 : NonNullable<Record<keyof T, string>>[K]
|
||||
>obj[key] : NonNullable<Record<keyof T, string>>[K]
|
||||
>obj : Record<keyof T, string> | undefined
|
||||
>key : K
|
||||
|
||||
const x2 = obj && obj[key];
|
||||
>x2 : Record<keyof T, string>[K]
|
||||
>obj && obj[key] : Record<keyof T, string>[K]
|
||||
>obj : T
|
||||
>obj[key] : Record<keyof T, string>[K]
|
||||
>obj : Record<keyof T, string>
|
||||
>key : K
|
||||
}
|
||||
|
||||
// Repro from #44166
|
||||
|
||||
class TableBaseEnum<
|
||||
>TableBaseEnum : TableBaseEnum<PublicSpec, InternalSpec>
|
||||
|
||||
PublicSpec extends Record<keyof InternalSpec, any>,
|
||||
InternalSpec extends Record<keyof PublicSpec, any> | undefined = undefined> {
|
||||
m() {
|
||||
>m : () => void
|
||||
|
||||
let iSpec = null! as InternalSpec;
|
||||
>iSpec : InternalSpec
|
||||
>null! as InternalSpec : InternalSpec
|
||||
>null! : never
|
||||
>null : null
|
||||
|
||||
iSpec[null! as keyof InternalSpec]; // Error, object possibly undefined
|
||||
>iSpec[null! as keyof InternalSpec] : NonNullable<Record<keyof PublicSpec, any>>[keyof InternalSpec]
|
||||
>iSpec : Record<keyof PublicSpec, any> | undefined
|
||||
>null! as keyof InternalSpec : keyof InternalSpec
|
||||
>null! : never
|
||||
>null : null
|
||||
|
||||
iSpec[null! as keyof PublicSpec]; // Error, object possibly undefined
|
||||
>iSpec[null! as keyof PublicSpec] : NonNullable<Record<keyof PublicSpec, any>>[keyof PublicSpec]
|
||||
>iSpec : Record<keyof PublicSpec, any> | undefined
|
||||
>null! as keyof PublicSpec : keyof PublicSpec
|
||||
>null! : never
|
||||
>null : null
|
||||
|
||||
if (iSpec === undefined) {
|
||||
>iSpec === undefined : boolean
|
||||
>iSpec : InternalSpec
|
||||
>undefined : undefined
|
||||
|
||||
return;
|
||||
}
|
||||
iSpec[null! as keyof InternalSpec];
|
||||
>iSpec[null! as keyof InternalSpec] : Record<keyof PublicSpec, any>[keyof InternalSpec]
|
||||
>iSpec : Record<keyof PublicSpec, any>
|
||||
>null! as keyof InternalSpec : keyof InternalSpec
|
||||
>null! : never
|
||||
>null : null
|
||||
|
||||
iSpec[null! as keyof PublicSpec];
|
||||
>iSpec[null! as keyof PublicSpec] : Record<keyof PublicSpec, any>[keyof PublicSpec]
|
||||
>iSpec : Record<keyof PublicSpec, any>
|
||||
>null! as keyof PublicSpec : keyof PublicSpec
|
||||
>null! : never
|
||||
>null : null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -139,3 +139,39 @@ function once<ET, T extends EventEmitter<ET>>(emittingObject: T, eventName: keyo
|
||||
emittingObject.off(eventName, 0);
|
||||
emittingObject.off(eventName as typeof eventName, 0);
|
||||
}
|
||||
|
||||
// In an element access obj[x], we consider obj to be in a constraint position, except when obj is of
|
||||
// a generic type without a nullable constraint and x is a generic type. This is because when both obj
|
||||
// and x are of generic types T and K, we want the resulting type to be T[K].
|
||||
|
||||
function fx1<T, K extends keyof T>(obj: T, key: K) {
|
||||
const x1 = obj[key];
|
||||
const x2 = obj && obj[key];
|
||||
}
|
||||
|
||||
function fx2<T extends Record<keyof T, string>, K extends keyof T>(obj: T, key: K) {
|
||||
const x1 = obj[key];
|
||||
const x2 = obj && obj[key];
|
||||
}
|
||||
|
||||
function fx3<T extends Record<keyof T, string> | undefined, K extends keyof T>(obj: T, key: K) {
|
||||
const x1 = obj[key]; // Error
|
||||
const x2 = obj && obj[key];
|
||||
}
|
||||
|
||||
// Repro from #44166
|
||||
|
||||
class TableBaseEnum<
|
||||
PublicSpec extends Record<keyof InternalSpec, any>,
|
||||
InternalSpec extends Record<keyof PublicSpec, any> | undefined = undefined> {
|
||||
m() {
|
||||
let iSpec = null! as InternalSpec;
|
||||
iSpec[null! as keyof InternalSpec]; // Error, object possibly undefined
|
||||
iSpec[null! as keyof PublicSpec]; // Error, object possibly undefined
|
||||
if (iSpec === undefined) {
|
||||
return;
|
||||
}
|
||||
iSpec[null! as keyof InternalSpec];
|
||||
iSpec[null! as keyof PublicSpec];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user