Merge pull request #24627 from Microsoft/typeofFunction

Fix typeof x === "function" type guards
This commit is contained in:
Anders Hejlsberg 2018-06-02 18:46:52 -07:00 committed by GitHub
commit 667de4bbb9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 566 additions and 1 deletions

View File

@ -14112,11 +14112,14 @@ namespace ts {
if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) {
assumeTrue = !assumeTrue;
}
if (type.flags & TypeFlags.Any && literal.text === "function") {
return type;
}
if (assumeTrue && !(type.flags & TypeFlags.Union)) {
// We narrow a non-union type to an exact primitive type if the non-union type
// is a supertype of that primitive type. For example, type 'any' can be narrowed
// to one of the primitive types.
const targetType = typeofTypesByName.get(literal.text);
const targetType = literal.text === "function" ? globalFunctionType : typeofTypesByName.get(literal.text);
if (targetType) {
if (isTypeSubtypeOf(targetType, type)) {
return targetType;

View File

@ -0,0 +1,139 @@
//// [typeGuardOfFormTypeOfFunction.ts]
function f1(x: any) {
if (typeof x === "function") {
x; // any
}
}
function f2(x: unknown) {
if (typeof x === "function") {
x; // Function
}
}
function f3(x: {}) {
if (typeof x === "function") {
x; // Function
}
}
function f4<T>(x: T) {
if (typeof x === "function") {
x; // T & Function
}
}
function f5(x: { s: string }) {
if (typeof x === "function") {
x; // never
}
}
function f6(x: () => string) {
if (typeof x === "function") {
x; // () => string
}
}
function f10(x: string | (() => string)) {
if (typeof x === "function") {
x; // () => string
}
else {
x; // string
}
}
function f11(x: { s: string } | (() => string)) {
if (typeof x === "function") {
x; // () => string
}
else {
x; // { s: string }
}
}
function f12(x: { s: string } | { n: number }) {
if (typeof x === "function") {
x; // never
}
else {
x; // { s: string } | { n: number }
}
}
// Repro from #18238
function f100<T, K extends keyof T>(obj: T, keys: K[]) : void {
for (const k of keys) {
const item = obj[k];
if (typeof item == 'function')
item.call(obj);
}
}
//// [typeGuardOfFormTypeOfFunction.js]
function f1(x) {
if (typeof x === "function") {
x; // any
}
}
function f2(x) {
if (typeof x === "function") {
x; // Function
}
}
function f3(x) {
if (typeof x === "function") {
x; // Function
}
}
function f4(x) {
if (typeof x === "function") {
x; // T & Function
}
}
function f5(x) {
if (typeof x === "function") {
x; // never
}
}
function f6(x) {
if (typeof x === "function") {
x; // () => string
}
}
function f10(x) {
if (typeof x === "function") {
x; // () => string
}
else {
x; // string
}
}
function f11(x) {
if (typeof x === "function") {
x; // () => string
}
else {
x; // { s: string }
}
}
function f12(x) {
if (typeof x === "function") {
x; // never
}
else {
x; // { s: string } | { n: number }
}
}
// Repro from #18238
function f100(obj, keys) {
for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
var k = keys_1[_i];
var item = obj[k];
if (typeof item == 'function')
item.call(obj);
}
}

View File

@ -0,0 +1,159 @@
=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfFunction.ts ===
function f1(x: any) {
>f1 : Symbol(f1, Decl(typeGuardOfFormTypeOfFunction.ts, 0, 0))
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 0, 12))
if (typeof x === "function") {
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 0, 12))
x; // any
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 0, 12))
}
}
function f2(x: unknown) {
>f2 : Symbol(f2, Decl(typeGuardOfFormTypeOfFunction.ts, 4, 1))
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 6, 12))
if (typeof x === "function") {
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 6, 12))
x; // Function
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 6, 12))
}
}
function f3(x: {}) {
>f3 : Symbol(f3, Decl(typeGuardOfFormTypeOfFunction.ts, 10, 1))
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 12, 12))
if (typeof x === "function") {
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 12, 12))
x; // Function
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 12, 12))
}
}
function f4<T>(x: T) {
>f4 : Symbol(f4, Decl(typeGuardOfFormTypeOfFunction.ts, 16, 1))
>T : Symbol(T, Decl(typeGuardOfFormTypeOfFunction.ts, 18, 12))
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 18, 15))
>T : Symbol(T, Decl(typeGuardOfFormTypeOfFunction.ts, 18, 12))
if (typeof x === "function") {
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 18, 15))
x; // T & Function
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 18, 15))
}
}
function f5(x: { s: string }) {
>f5 : Symbol(f5, Decl(typeGuardOfFormTypeOfFunction.ts, 22, 1))
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 24, 12))
>s : Symbol(s, Decl(typeGuardOfFormTypeOfFunction.ts, 24, 16))
if (typeof x === "function") {
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 24, 12))
x; // never
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 24, 12))
}
}
function f6(x: () => string) {
>f6 : Symbol(f6, Decl(typeGuardOfFormTypeOfFunction.ts, 28, 1))
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 30, 12))
if (typeof x === "function") {
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 30, 12))
x; // () => string
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 30, 12))
}
}
function f10(x: string | (() => string)) {
>f10 : Symbol(f10, Decl(typeGuardOfFormTypeOfFunction.ts, 34, 1))
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 36, 13))
if (typeof x === "function") {
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 36, 13))
x; // () => string
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 36, 13))
}
else {
x; // string
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 36, 13))
}
}
function f11(x: { s: string } | (() => string)) {
>f11 : Symbol(f11, Decl(typeGuardOfFormTypeOfFunction.ts, 43, 1))
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 45, 13))
>s : Symbol(s, Decl(typeGuardOfFormTypeOfFunction.ts, 45, 17))
if (typeof x === "function") {
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 45, 13))
x; // () => string
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 45, 13))
}
else {
x; // { s: string }
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 45, 13))
}
}
function f12(x: { s: string } | { n: number }) {
>f12 : Symbol(f12, Decl(typeGuardOfFormTypeOfFunction.ts, 52, 1))
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 54, 13))
>s : Symbol(s, Decl(typeGuardOfFormTypeOfFunction.ts, 54, 17))
>n : Symbol(n, Decl(typeGuardOfFormTypeOfFunction.ts, 54, 33))
if (typeof x === "function") {
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 54, 13))
x; // never
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 54, 13))
}
else {
x; // { s: string } | { n: number }
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 54, 13))
}
}
// Repro from #18238
function f100<T, K extends keyof T>(obj: T, keys: K[]) : void {
>f100 : Symbol(f100, Decl(typeGuardOfFormTypeOfFunction.ts, 61, 1))
>T : Symbol(T, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 14))
>K : Symbol(K, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 16))
>T : Symbol(T, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 14))
>obj : Symbol(obj, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 36))
>T : Symbol(T, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 14))
>keys : Symbol(keys, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 43))
>K : Symbol(K, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 16))
for (const k of keys) {
>k : Symbol(k, Decl(typeGuardOfFormTypeOfFunction.ts, 66, 14))
>keys : Symbol(keys, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 43))
const item = obj[k];
>item : Symbol(item, Decl(typeGuardOfFormTypeOfFunction.ts, 67, 13))
>obj : Symbol(obj, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 36))
>k : Symbol(k, Decl(typeGuardOfFormTypeOfFunction.ts, 66, 14))
if (typeof item == 'function')
>item : Symbol(item, Decl(typeGuardOfFormTypeOfFunction.ts, 67, 13))
item.call(obj);
>item.call : Symbol(Function.call, Decl(lib.d.ts, --, --))
>item : Symbol(item, Decl(typeGuardOfFormTypeOfFunction.ts, 67, 13))
>call : Symbol(Function.call, Decl(lib.d.ts, --, --))
>obj : Symbol(obj, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 36))
}
}

View File

@ -0,0 +1,191 @@
=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfFunction.ts ===
function f1(x: any) {
>f1 : (x: any) => void
>x : any
if (typeof x === "function") {
>typeof x === "function" : boolean
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : any
>"function" : "function"
x; // any
>x : any
}
}
function f2(x: unknown) {
>f2 : (x: unknown) => void
>x : unknown
if (typeof x === "function") {
>typeof x === "function" : boolean
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : unknown
>"function" : "function"
x; // Function
>x : Function
}
}
function f3(x: {}) {
>f3 : (x: {}) => void
>x : {}
if (typeof x === "function") {
>typeof x === "function" : boolean
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : {}
>"function" : "function"
x; // Function
>x : Function
}
}
function f4<T>(x: T) {
>f4 : <T>(x: T) => void
>T : T
>x : T
>T : T
if (typeof x === "function") {
>typeof x === "function" : boolean
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : T
>"function" : "function"
x; // T & Function
>x : T & Function
}
}
function f5(x: { s: string }) {
>f5 : (x: { s: string; }) => void
>x : { s: string; }
>s : string
if (typeof x === "function") {
>typeof x === "function" : boolean
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : { s: string; }
>"function" : "function"
x; // never
>x : never
}
}
function f6(x: () => string) {
>f6 : (x: () => string) => void
>x : () => string
if (typeof x === "function") {
>typeof x === "function" : boolean
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : () => string
>"function" : "function"
x; // () => string
>x : () => string
}
}
function f10(x: string | (() => string)) {
>f10 : (x: string | (() => string)) => void
>x : string | (() => string)
if (typeof x === "function") {
>typeof x === "function" : boolean
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : string | (() => string)
>"function" : "function"
x; // () => string
>x : () => string
}
else {
x; // string
>x : string
}
}
function f11(x: { s: string } | (() => string)) {
>f11 : (x: { s: string; } | (() => string)) => void
>x : { s: string; } | (() => string)
>s : string
if (typeof x === "function") {
>typeof x === "function" : boolean
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : { s: string; } | (() => string)
>"function" : "function"
x; // () => string
>x : () => string
}
else {
x; // { s: string }
>x : { s: string; }
}
}
function f12(x: { s: string } | { n: number }) {
>f12 : (x: { s: string; } | { n: number; }) => void
>x : { s: string; } | { n: number; }
>s : string
>n : number
if (typeof x === "function") {
>typeof x === "function" : boolean
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : { s: string; } | { n: number; }
>"function" : "function"
x; // never
>x : never
}
else {
x; // { s: string } | { n: number }
>x : { s: string; } | { n: number; }
}
}
// Repro from #18238
function f100<T, K extends keyof T>(obj: T, keys: K[]) : void {
>f100 : <T, K extends keyof T>(obj: T, keys: K[]) => void
>T : T
>K : K
>T : T
>obj : T
>T : T
>keys : K[]
>K : K
for (const k of keys) {
>k : K
>keys : K[]
const item = obj[k];
>item : T[K]
>obj[k] : T[K]
>obj : T
>k : K
if (typeof item == 'function')
>typeof item == 'function' : boolean
>typeof item : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>item : T[K]
>'function' : "function"
item.call(obj);
>item.call(obj) : any
>item.call : (this: Function, thisArg: any, ...argArray: any[]) => any
>item : T[K] & Function
>call : (this: Function, thisArg: any, ...argArray: any[]) => any
>obj : T
}
}

View File

@ -0,0 +1,73 @@
function f1(x: any) {
if (typeof x === "function") {
x; // any
}
}
function f2(x: unknown) {
if (typeof x === "function") {
x; // Function
}
}
function f3(x: {}) {
if (typeof x === "function") {
x; // Function
}
}
function f4<T>(x: T) {
if (typeof x === "function") {
x; // T & Function
}
}
function f5(x: { s: string }) {
if (typeof x === "function") {
x; // never
}
}
function f6(x: () => string) {
if (typeof x === "function") {
x; // () => string
}
}
function f10(x: string | (() => string)) {
if (typeof x === "function") {
x; // () => string
}
else {
x; // string
}
}
function f11(x: { s: string } | (() => string)) {
if (typeof x === "function") {
x; // () => string
}
else {
x; // { s: string }
}
}
function f12(x: { s: string } | { n: number }) {
if (typeof x === "function") {
x; // never
}
else {
x; // { s: string } | { n: number }
}
}
// Repro from #18238
function f100<T, K extends keyof T>(obj: T, keys: K[]) : void {
for (const k of keys) {
const item = obj[k];
if (typeof item == 'function')
item.call(obj);
}
}