Add narrowing for unknown with strict equal

This commit is contained in:
Jack Williams 2018-09-06 16:56:31 +01:00
parent d16cf761ba
commit 267e5989cd
5 changed files with 408 additions and 0 deletions

View file

@ -16018,6 +16018,9 @@ namespace ts {
assumeTrue = !assumeTrue;
}
const valueType = getTypeOfExpression(value);
if ((type.flags & TypeFlags.Unknown) && (operator === SyntaxKind.EqualsEqualsEqualsToken) && (valueType.flags & TypeFlags.Unit)) {
return assumeTrue ? valueType : type;
}
if (valueType.flags & TypeFlags.Nullable) {
if (!strictNullChecks) {
return type;

View file

@ -0,0 +1,82 @@
//// [unknownType2.ts]
type isUnknown<T> = unknown extends T ? true : false;
type isTrue<T extends true> = T;
type SomeResponse = 'yes' | 'no' | 'idk';
let validate: (x: unknown) => SomeResponse = x => (x === 'yes' || x === 'no') ? x : 'idk'; // No error
const u: unknown = undefined;
declare const symb: unique symbol;
if (u === 5) {
const y = u.toString(10);
}
if (u === true || u === false) {
const someBool: boolean = u;
}
if (u === undefined) {
const undef: undefined = u;
}
if (u === null) {
const someNull: null = u;
}
if(u === symb) {
const symbolAlias: typeof symb = u;
}
if (!(u === 42)) {
u // u should still be `unknown` here
}
if (u !== 42) {
type A = isTrue<isUnknown<typeof u>>
}
if (u == 42) {
type B = isTrue<isUnknown<typeof u>>
}
if (u == true) {
type C = isTrue<isUnknown<typeof u>>
}
if (u == Object) {
type D = isTrue<isUnknown<typeof u>>
}
//// [unknownType2.js]
"use strict";
var validate = function (x) { return (x === 'yes' || x === 'no') ? x : 'idk'; }; // No error
var u = undefined;
if (u === 5) {
var y = u.toString(10);
}
if (u === true || u === false) {
var someBool = u;
}
if (u === undefined) {
var undef = u;
}
if (u === null) {
var someNull = u;
}
if (u === symb) {
var symbolAlias = u;
}
if (!(u === 42)) {
u; // u should still be `unknown` here
}
if (u !== 42) {
}
if (u == 42) {
}
if (u == true) {
}
if (u == Object) {
}

View file

@ -0,0 +1,124 @@
=== tests/cases/conformance/types/unknown/unknownType2.ts ===
type isUnknown<T> = unknown extends T ? true : false;
>isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0))
>T : Symbol(T, Decl(unknownType2.ts, 0, 15))
>T : Symbol(T, Decl(unknownType2.ts, 0, 15))
type isTrue<T extends true> = T;
>isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53))
>T : Symbol(T, Decl(unknownType2.ts, 1, 12))
>T : Symbol(T, Decl(unknownType2.ts, 1, 12))
type SomeResponse = 'yes' | 'no' | 'idk';
>SomeResponse : Symbol(SomeResponse, Decl(unknownType2.ts, 1, 32))
let validate: (x: unknown) => SomeResponse = x => (x === 'yes' || x === 'no') ? x : 'idk'; // No error
>validate : Symbol(validate, Decl(unknownType2.ts, 4, 3))
>x : Symbol(x, Decl(unknownType2.ts, 4, 15))
>SomeResponse : Symbol(SomeResponse, Decl(unknownType2.ts, 1, 32))
>x : Symbol(x, Decl(unknownType2.ts, 4, 44))
>x : Symbol(x, Decl(unknownType2.ts, 4, 44))
>x : Symbol(x, Decl(unknownType2.ts, 4, 44))
>x : Symbol(x, Decl(unknownType2.ts, 4, 44))
const u: unknown = undefined;
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
>undefined : Symbol(undefined)
declare const symb: unique symbol;
>symb : Symbol(symb, Decl(unknownType2.ts, 8, 13))
if (u === 5) {
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
const y = u.toString(10);
>y : Symbol(y, Decl(unknownType2.ts, 11, 9))
>u.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))
}
if (u === true || u === false) {
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
const someBool: boolean = u;
>someBool : Symbol(someBool, Decl(unknownType2.ts, 15, 9))
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
}
if (u === undefined) {
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
>undefined : Symbol(undefined)
const undef: undefined = u;
>undef : Symbol(undef, Decl(unknownType2.ts, 19, 9))
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
}
if (u === null) {
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
const someNull: null = u;
>someNull : Symbol(someNull, Decl(unknownType2.ts, 23, 9))
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
}
if(u === symb) {
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
>symb : Symbol(symb, Decl(unknownType2.ts, 8, 13))
const symbolAlias: typeof symb = u;
>symbolAlias : Symbol(symbolAlias, Decl(unknownType2.ts, 27, 9))
>symb : Symbol(symb, Decl(unknownType2.ts, 8, 13))
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
}
if (!(u === 42)) {
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
u // u should still be `unknown` here
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
}
if (u !== 42) {
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
type A = isTrue<isUnknown<typeof u>>
>A : Symbol(A, Decl(unknownType2.ts, 34, 15))
>isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53))
>isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0))
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
}
if (u == 42) {
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
type B = isTrue<isUnknown<typeof u>>
>B : Symbol(B, Decl(unknownType2.ts, 38, 14))
>isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53))
>isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0))
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
}
if (u == true) {
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
type C = isTrue<isUnknown<typeof u>>
>C : Symbol(C, Decl(unknownType2.ts, 42, 16))
>isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53))
>isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0))
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
}
if (u == Object) {
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
type D = isTrue<isUnknown<typeof u>>
>D : Symbol(D, Decl(unknownType2.ts, 46, 18))
>isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53))
>isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0))
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
}

View file

@ -0,0 +1,148 @@
=== tests/cases/conformance/types/unknown/unknownType2.ts ===
type isUnknown<T> = unknown extends T ? true : false;
>isUnknown : isUnknown<T>
>true : true
>false : false
type isTrue<T extends true> = T;
>isTrue : T
>true : true
type SomeResponse = 'yes' | 'no' | 'idk';
>SomeResponse : SomeResponse
let validate: (x: unknown) => SomeResponse = x => (x === 'yes' || x === 'no') ? x : 'idk'; // No error
>validate : (x: unknown) => SomeResponse
>x : unknown
>x => (x === 'yes' || x === 'no') ? x : 'idk' : (x: unknown) => "yes" | "no" | "idk"
>x : unknown
>(x === 'yes' || x === 'no') ? x : 'idk' : SomeResponse
>(x === 'yes' || x === 'no') : boolean
>x === 'yes' || x === 'no' : boolean
>x === 'yes' : boolean
>x : unknown
>'yes' : "yes"
>x === 'no' : boolean
>x : unknown
>'no' : "no"
>x : "yes" | "no"
>'idk' : "idk"
const u: unknown = undefined;
>u : unknown
>undefined : undefined
declare const symb: unique symbol;
>symb : unique symbol
if (u === 5) {
>u === 5 : boolean
>u : unknown
>5 : 5
const y = u.toString(10);
>y : string
>u.toString(10) : string
>u.toString : (radix?: number | undefined) => string
>u : 5
>toString : (radix?: number | undefined) => string
>10 : 10
}
if (u === true || u === false) {
>u === true || u === false : boolean
>u === true : boolean
>u : unknown
>true : true
>u === false : boolean
>u : unknown
>false : false
const someBool: boolean = u;
>someBool : boolean
>u : boolean
}
if (u === undefined) {
>u === undefined : boolean
>u : unknown
>undefined : undefined
const undef: undefined = u;
>undef : undefined
>u : undefined
}
if (u === null) {
>u === null : boolean
>u : unknown
>null : null
const someNull: null = u;
>someNull : null
>null : null
>u : null
}
if(u === symb) {
>u === symb : boolean
>u : unknown
>symb : unique symbol
const symbolAlias: typeof symb = u;
>symbolAlias : unique symbol
>symb : unique symbol
>u : unique symbol
}
if (!(u === 42)) {
>!(u === 42) : boolean
>(u === 42) : boolean
>u === 42 : boolean
>u : unknown
>42 : 42
u // u should still be `unknown` here
>u : unknown
}
if (u !== 42) {
>u !== 42 : boolean
>u : unknown
>42 : 42
type A = isTrue<isUnknown<typeof u>>
>A : true
>u : unknown
}
if (u == 42) {
>u == 42 : boolean
>u : unknown
>42 : 42
type B = isTrue<isUnknown<typeof u>>
>B : true
>u : unknown
}
if (u == true) {
>u == true : boolean
>u : unknown
>true : true
type C = isTrue<isUnknown<typeof u>>
>C : true
>u : unknown
}
if (u == Object) {
>u == Object : boolean
>u : unknown
>Object : ObjectConstructor
type D = isTrue<isUnknown<typeof u>>
>D : true
>u : unknown
}

View file

@ -0,0 +1,51 @@
// @strict: true
type isUnknown<T> = unknown extends T ? true : false;
type isTrue<T extends true> = T;
type SomeResponse = 'yes' | 'no' | 'idk';
let validate: (x: unknown) => SomeResponse = x => (x === 'yes' || x === 'no') ? x : 'idk'; // No error
const u: unknown = undefined;
declare const symb: unique symbol;
if (u === 5) {
const y = u.toString(10);
}
if (u === true || u === false) {
const someBool: boolean = u;
}
if (u === undefined) {
const undef: undefined = u;
}
if (u === null) {
const someNull: null = u;
}
if(u === symb) {
const symbolAlias: typeof symb = u;
}
if (!(u === 42)) {
u // u should still be `unknown` here
}
if (u !== 42) {
type A = isTrue<isUnknown<typeof u>>
}
if (u == 42) {
type B = isTrue<isUnknown<typeof u>>
}
if (u == true) {
type C = isTrue<isUnknown<typeof u>>
}
if (u == Object) {
type D = isTrue<isUnknown<typeof u>>
}