Track non-null unknown types in control flow analysis (#45575)
* Track non-null unknown types in CFA * Add tests
This commit is contained in:
parent
630012a6a7
commit
5186ee3d1b
|
@ -755,6 +755,7 @@ namespace ts {
|
|||
const nonInferrableAnyType = createIntrinsicType(TypeFlags.Any, "any", ObjectFlags.ContainsWideningType);
|
||||
const intrinsicMarkerType = createIntrinsicType(TypeFlags.Any, "intrinsic");
|
||||
const unknownType = createIntrinsicType(TypeFlags.Unknown, "unknown");
|
||||
const nonNullUnknownType = createIntrinsicType(TypeFlags.Unknown, "unknown");
|
||||
const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined");
|
||||
const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined, "undefined", ObjectFlags.ContainsWideningType);
|
||||
const optionalType = createIntrinsicType(TypeFlags.Undefined, "undefined");
|
||||
|
@ -14048,7 +14049,9 @@ namespace ts {
|
|||
const includes = addTypesToUnion(typeSet, 0, types);
|
||||
if (unionReduction !== UnionReduction.None) {
|
||||
if (includes & TypeFlags.AnyOrUnknown) {
|
||||
return includes & TypeFlags.Any ? includes & TypeFlags.IncludesWildcard ? wildcardType : anyType : unknownType;
|
||||
return includes & TypeFlags.Any ?
|
||||
includes & TypeFlags.IncludesWildcard ? wildcardType : anyType :
|
||||
includes & TypeFlags.Null || containsType(typeSet, unknownType) ? unknownType : nonNullUnknownType;
|
||||
}
|
||||
if (exactOptionalPropertyTypes && includes & TypeFlags.Undefined) {
|
||||
const missingIndex = binarySearch(typeSet, missingType, getTypeId, compareValues);
|
||||
|
@ -22220,13 +22223,6 @@ namespace ts {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Given a source x, check if target matches x or is an && operation with an operand that matches x.
|
||||
function containsTruthyCheck(source: Node, target: Node): boolean {
|
||||
return isMatchingReference(source, target) ||
|
||||
(target.kind === SyntaxKind.BinaryExpression && (target as BinaryExpression).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken &&
|
||||
(containsTruthyCheck(source, (target as BinaryExpression).left) || containsTruthyCheck(source, (target as BinaryExpression).right)));
|
||||
}
|
||||
|
||||
function getPropertyAccess(expr: Expression) {
|
||||
if (isAccessExpression(expr)) {
|
||||
return expr;
|
||||
|
@ -23239,7 +23235,8 @@ namespace ts {
|
|||
if (resultType === unreachableNeverType || reference.parent && reference.parent.kind === SyntaxKind.NonNullExpression && !(resultType.flags & TypeFlags.Never) && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) {
|
||||
return declaredType;
|
||||
}
|
||||
return resultType;
|
||||
// The non-null unknown type should never escape control flow analysis.
|
||||
return resultType === nonNullUnknownType ? unknownType : resultType;
|
||||
|
||||
function getOrSetCacheKey() {
|
||||
if (isKeySet) {
|
||||
|
@ -23727,7 +23724,8 @@ namespace ts {
|
|||
|
||||
function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type {
|
||||
if (isMatchingReference(reference, expr)) {
|
||||
return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy);
|
||||
return type.flags & TypeFlags.Unknown && assumeTrue ? nonNullUnknownType :
|
||||
getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy);
|
||||
}
|
||||
if (strictNullChecks && assumeTrue && optionalChainContainsReference(expr, reference)) {
|
||||
type = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull);
|
||||
|
@ -23885,7 +23883,7 @@ namespace ts {
|
|||
valueType.flags & TypeFlags.Null ?
|
||||
assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull :
|
||||
assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined;
|
||||
return getTypeWithFacts(type, facts);
|
||||
return type.flags & TypeFlags.Unknown && facts & (TypeFacts.NENull | TypeFacts.NEUndefinedOrNull) ? nonNullUnknownType : getTypeWithFacts(type, facts);
|
||||
}
|
||||
if (assumeTrue) {
|
||||
const filterFn: (t: Type) => boolean = operator === SyntaxKind.EqualsEqualsToken ?
|
||||
|
@ -23915,15 +23913,10 @@ namespace ts {
|
|||
return type;
|
||||
}
|
||||
if (assumeTrue && type.flags & TypeFlags.Unknown && literal.text === "object") {
|
||||
// The pattern x && typeof x === 'object', where x is of type unknown, narrows x to type object. We don't
|
||||
// need to check for the reverse typeof x === 'object' && x since that already narrows correctly.
|
||||
if (typeOfExpr.parent.parent.kind === SyntaxKind.BinaryExpression) {
|
||||
const expr = typeOfExpr.parent.parent as BinaryExpression;
|
||||
if (expr.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken && expr.right === typeOfExpr.parent && containsTruthyCheck(reference, expr.left)) {
|
||||
return nonPrimitiveType;
|
||||
}
|
||||
}
|
||||
return getUnionType([nonPrimitiveType, nullType]);
|
||||
// The non-null unknown type is used to track whether a previous narrowing operation has removed the null type
|
||||
// from the unknown type. For example, the expression `x && typeof x === 'object'` first narrows x to the non-null
|
||||
// unknown type, and then narrows that to the non-primitive type.
|
||||
return type === nonNullUnknownType ? nonPrimitiveType : getUnionType([nonPrimitiveType, nullType]);
|
||||
}
|
||||
const facts = assumeTrue ?
|
||||
typeofEQFacts.get(literal.text) || TypeFacts.TypeofEQHostObject :
|
||||
|
|
77
tests/baselines/reference/controlFlowTypeofObject.errors.txt
Normal file
77
tests/baselines/reference/controlFlowTypeofObject.errors.txt
Normal file
|
@ -0,0 +1,77 @@
|
|||
tests/cases/conformance/controlFlow/controlFlowTypeofObject.ts(66,13): error TS2345: Argument of type 'object | null' is not assignable to parameter of type 'object'.
|
||||
Type 'null' is not assignable to type 'object'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/controlFlow/controlFlowTypeofObject.ts (1 errors) ====
|
||||
declare function obj(x: object): void;
|
||||
|
||||
function f1(x: unknown) {
|
||||
if (!x) {
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
|
||||
function f2(x: unknown) {
|
||||
if (x === null) {
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
|
||||
function f3(x: unknown) {
|
||||
if (x == null) {
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
|
||||
function f4(x: unknown) {
|
||||
if (x == undefined) {
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
|
||||
function f5(x: unknown) {
|
||||
if (!!true) {
|
||||
if (!x) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (x === null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
|
||||
function f6(x: unknown) {
|
||||
if (x === null) {
|
||||
x;
|
||||
}
|
||||
else {
|
||||
x;
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x); // Error
|
||||
~
|
||||
!!! error TS2345: Argument of type 'object | null' is not assignable to parameter of type 'object'.
|
||||
!!! error TS2345: Type 'null' is not assignable to type 'object'.
|
||||
}
|
||||
}
|
||||
|
144
tests/baselines/reference/controlFlowTypeofObject.js
Normal file
144
tests/baselines/reference/controlFlowTypeofObject.js
Normal file
|
@ -0,0 +1,144 @@
|
|||
//// [controlFlowTypeofObject.ts]
|
||||
declare function obj(x: object): void;
|
||||
|
||||
function f1(x: unknown) {
|
||||
if (!x) {
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
|
||||
function f2(x: unknown) {
|
||||
if (x === null) {
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
|
||||
function f3(x: unknown) {
|
||||
if (x == null) {
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
|
||||
function f4(x: unknown) {
|
||||
if (x == undefined) {
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
|
||||
function f5(x: unknown) {
|
||||
if (!!true) {
|
||||
if (!x) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (x === null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
|
||||
function f6(x: unknown) {
|
||||
if (x === null) {
|
||||
x;
|
||||
}
|
||||
else {
|
||||
x;
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x); // Error
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//// [controlFlowTypeofObject.js]
|
||||
"use strict";
|
||||
function f1(x) {
|
||||
if (!x) {
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
function f2(x) {
|
||||
if (x === null) {
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
function f3(x) {
|
||||
if (x == null) {
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
function f4(x) {
|
||||
if (x == undefined) {
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
function f5(x) {
|
||||
if (!!true) {
|
||||
if (!x) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (x === null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
function f6(x) {
|
||||
if (x === null) {
|
||||
x;
|
||||
}
|
||||
else {
|
||||
x;
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x); // Error
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//// [controlFlowTypeofObject.d.ts]
|
||||
declare function obj(x: object): void;
|
||||
declare function f1(x: unknown): void;
|
||||
declare function f2(x: unknown): void;
|
||||
declare function f3(x: unknown): void;
|
||||
declare function f4(x: unknown): void;
|
||||
declare function f5(x: unknown): void;
|
||||
declare function f6(x: unknown): void;
|
136
tests/baselines/reference/controlFlowTypeofObject.symbols
Normal file
136
tests/baselines/reference/controlFlowTypeofObject.symbols
Normal file
|
@ -0,0 +1,136 @@
|
|||
=== tests/cases/conformance/controlFlow/controlFlowTypeofObject.ts ===
|
||||
declare function obj(x: object): void;
|
||||
>obj : Symbol(obj, Decl(controlFlowTypeofObject.ts, 0, 0))
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 0, 21))
|
||||
|
||||
function f1(x: unknown) {
|
||||
>f1 : Symbol(f1, Decl(controlFlowTypeofObject.ts, 0, 38))
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 2, 12))
|
||||
|
||||
if (!x) {
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 2, 12))
|
||||
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 2, 12))
|
||||
|
||||
obj(x);
|
||||
>obj : Symbol(obj, Decl(controlFlowTypeofObject.ts, 0, 0))
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 2, 12))
|
||||
}
|
||||
}
|
||||
|
||||
function f2(x: unknown) {
|
||||
>f2 : Symbol(f2, Decl(controlFlowTypeofObject.ts, 9, 1))
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 11, 12))
|
||||
|
||||
if (x === null) {
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 11, 12))
|
||||
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 11, 12))
|
||||
|
||||
obj(x);
|
||||
>obj : Symbol(obj, Decl(controlFlowTypeofObject.ts, 0, 0))
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 11, 12))
|
||||
}
|
||||
}
|
||||
|
||||
function f3(x: unknown) {
|
||||
>f3 : Symbol(f3, Decl(controlFlowTypeofObject.ts, 18, 1))
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 20, 12))
|
||||
|
||||
if (x == null) {
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 20, 12))
|
||||
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 20, 12))
|
||||
|
||||
obj(x);
|
||||
>obj : Symbol(obj, Decl(controlFlowTypeofObject.ts, 0, 0))
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 20, 12))
|
||||
}
|
||||
}
|
||||
|
||||
function f4(x: unknown) {
|
||||
>f4 : Symbol(f4, Decl(controlFlowTypeofObject.ts, 27, 1))
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 29, 12))
|
||||
|
||||
if (x == undefined) {
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 29, 12))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 29, 12))
|
||||
|
||||
obj(x);
|
||||
>obj : Symbol(obj, Decl(controlFlowTypeofObject.ts, 0, 0))
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 29, 12))
|
||||
}
|
||||
}
|
||||
|
||||
function f5(x: unknown) {
|
||||
>f5 : Symbol(f5, Decl(controlFlowTypeofObject.ts, 36, 1))
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 38, 12))
|
||||
|
||||
if (!!true) {
|
||||
if (!x) {
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 38, 12))
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (x === null) {
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 38, 12))
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 38, 12))
|
||||
|
||||
obj(x);
|
||||
>obj : Symbol(obj, Decl(controlFlowTypeofObject.ts, 0, 0))
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 38, 12))
|
||||
}
|
||||
}
|
||||
|
||||
function f6(x: unknown) {
|
||||
>f6 : Symbol(f6, Decl(controlFlowTypeofObject.ts, 52, 1))
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 54, 12))
|
||||
|
||||
if (x === null) {
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 54, 12))
|
||||
|
||||
x;
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 54, 12))
|
||||
}
|
||||
else {
|
||||
x;
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 54, 12))
|
||||
|
||||
if (typeof x === 'object') {
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 54, 12))
|
||||
|
||||
obj(x);
|
||||
>obj : Symbol(obj, Decl(controlFlowTypeofObject.ts, 0, 0))
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 54, 12))
|
||||
}
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 54, 12))
|
||||
|
||||
obj(x); // Error
|
||||
>obj : Symbol(obj, Decl(controlFlowTypeofObject.ts, 0, 0))
|
||||
>x : Symbol(x, Decl(controlFlowTypeofObject.ts, 54, 12))
|
||||
}
|
||||
}
|
||||
|
179
tests/baselines/reference/controlFlowTypeofObject.types
Normal file
179
tests/baselines/reference/controlFlowTypeofObject.types
Normal file
|
@ -0,0 +1,179 @@
|
|||
=== tests/cases/conformance/controlFlow/controlFlowTypeofObject.ts ===
|
||||
declare function obj(x: object): void;
|
||||
>obj : (x: object) => void
|
||||
>x : object
|
||||
|
||||
function f1(x: unknown) {
|
||||
>f1 : (x: unknown) => void
|
||||
>x : unknown
|
||||
|
||||
if (!x) {
|
||||
>!x : boolean
|
||||
>x : unknown
|
||||
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
>typeof x === 'object' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : unknown
|
||||
>'object' : "object"
|
||||
|
||||
obj(x);
|
||||
>obj(x) : void
|
||||
>obj : (x: object) => void
|
||||
>x : object
|
||||
}
|
||||
}
|
||||
|
||||
function f2(x: unknown) {
|
||||
>f2 : (x: unknown) => void
|
||||
>x : unknown
|
||||
|
||||
if (x === null) {
|
||||
>x === null : boolean
|
||||
>x : unknown
|
||||
>null : null
|
||||
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
>typeof x === 'object' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : unknown
|
||||
>'object' : "object"
|
||||
|
||||
obj(x);
|
||||
>obj(x) : void
|
||||
>obj : (x: object) => void
|
||||
>x : object
|
||||
}
|
||||
}
|
||||
|
||||
function f3(x: unknown) {
|
||||
>f3 : (x: unknown) => void
|
||||
>x : unknown
|
||||
|
||||
if (x == null) {
|
||||
>x == null : boolean
|
||||
>x : unknown
|
||||
>null : null
|
||||
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
>typeof x === 'object' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : unknown
|
||||
>'object' : "object"
|
||||
|
||||
obj(x);
|
||||
>obj(x) : void
|
||||
>obj : (x: object) => void
|
||||
>x : object
|
||||
}
|
||||
}
|
||||
|
||||
function f4(x: unknown) {
|
||||
>f4 : (x: unknown) => void
|
||||
>x : unknown
|
||||
|
||||
if (x == undefined) {
|
||||
>x == undefined : boolean
|
||||
>x : unknown
|
||||
>undefined : undefined
|
||||
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
>typeof x === 'object' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : unknown
|
||||
>'object' : "object"
|
||||
|
||||
obj(x);
|
||||
>obj(x) : void
|
||||
>obj : (x: object) => void
|
||||
>x : object
|
||||
}
|
||||
}
|
||||
|
||||
function f5(x: unknown) {
|
||||
>f5 : (x: unknown) => void
|
||||
>x : unknown
|
||||
|
||||
if (!!true) {
|
||||
>!!true : true
|
||||
>!true : false
|
||||
>true : true
|
||||
|
||||
if (!x) {
|
||||
>!x : boolean
|
||||
>x : unknown
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (x === null) {
|
||||
>x === null : boolean
|
||||
>x : unknown
|
||||
>null : null
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
>typeof x === 'object' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : unknown
|
||||
>'object' : "object"
|
||||
|
||||
obj(x);
|
||||
>obj(x) : void
|
||||
>obj : (x: object) => void
|
||||
>x : object
|
||||
}
|
||||
}
|
||||
|
||||
function f6(x: unknown) {
|
||||
>f6 : (x: unknown) => void
|
||||
>x : unknown
|
||||
|
||||
if (x === null) {
|
||||
>x === null : boolean
|
||||
>x : unknown
|
||||
>null : null
|
||||
|
||||
x;
|
||||
>x : null
|
||||
}
|
||||
else {
|
||||
x;
|
||||
>x : unknown
|
||||
|
||||
if (typeof x === 'object') {
|
||||
>typeof x === 'object' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : unknown
|
||||
>'object' : "object"
|
||||
|
||||
obj(x);
|
||||
>obj(x) : void
|
||||
>obj : (x: object) => void
|
||||
>x : object
|
||||
}
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
>typeof x === 'object' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : unknown
|
||||
>'object' : "object"
|
||||
|
||||
obj(x); // Error
|
||||
>obj(x) : void
|
||||
>obj : (x: object) => void
|
||||
>x : object | null
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
// @strict: true
|
||||
// @declaration: true
|
||||
|
||||
declare function obj(x: object): void;
|
||||
|
||||
function f1(x: unknown) {
|
||||
if (!x) {
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
|
||||
function f2(x: unknown) {
|
||||
if (x === null) {
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
|
||||
function f3(x: unknown) {
|
||||
if (x == null) {
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
|
||||
function f4(x: unknown) {
|
||||
if (x == undefined) {
|
||||
return;
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
|
||||
function f5(x: unknown) {
|
||||
if (!!true) {
|
||||
if (!x) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (x === null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
|
||||
function f6(x: unknown) {
|
||||
if (x === null) {
|
||||
x;
|
||||
}
|
||||
else {
|
||||
x;
|
||||
if (typeof x === 'object') {
|
||||
obj(x);
|
||||
}
|
||||
}
|
||||
if (typeof x === 'object') {
|
||||
obj(x); // Error
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue