简体   繁体   中英

TypeScript function passes type check when not expected to

I have a function identityOrCall that either calls the function given to it, or returns the value. value is a generic type.

function identityOrCall <T>(value: T): T {
  if (typeof value === 'function')
    return value()
  else
    return value
}

identityOrCall(() => 'x') // -> 'x'

The line identityOrCall(() => 'x') appears to pass the compiler's type checking.

Why? Shouldn't it give an error?

If identityOrCall is passed a function, I would expect the generic type T to be set to Function and that identityOrCall should return a Function type.

Instead I'm able to pass a Function type as an argument and have identityOrCall return a string type.

Why is this? It appears inconsistent to me, but I must be missing something.

The problem is that, since the function return type is not noted anywhere, TypeScript takes its type to be any . any is not type-safe; it's assignable to anything. Here, TS extracts T from the any return value (since any can be narrowed down to T).

Best to avoid types of any whenever possible. I like using TSLint's no-unsafe-any which forbids this sort of thing.

For similar reasons, the following does not throw a TypeScript error (and is prohibited using the above linting rule), even though it's clearly unsafe:

declare const fn: () => any;
function identityOrCall<T>(value: T): T {
    return fn();
}

const result = identityOrCall(() => 'x') // -> 'x'
// result is typed as: () => 'x'. Huh?

If the goal is to have a method behave differently based on the parameters I think the easiest way is to specify overloads

function identityOrCall<T>(value: () => T): T;
function identityOrCall<T>(value: T): T;

function identityOrCall <T>(value: () => T|T): T {
  if (typeof value === 'function')
    return value()
  else
    return value
}

const result = identityOrCall(() => 'x') // first overload
const result2 = identityOrCall('x') // second overload

result.split(' ');  // No error
result2.split(' '); // No error
result(); // error not callable

When calling the method TS will resolve the signature by going through the overloads until it finds one which matches (meaning order of defining overloads matters here).

Playground link

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM