简体   繁体   中英

TypeScript: is it possible to infer a type based on whether a function call succeeds?

This is a distilled example based on much more complicated legacy code. Suppose you have a function like this:

function foo(val: string | number): void {
  if (typeof(val) !== 'number') {
    throw new Error('val must be a number');
  }
  // do something with number
}

Is it possible to write the signature of foo such that after calling it, the compiler infers that val must be a number?

const val: string | number = getVal();

foo(val);

// Compiler shouldn't complain here since a string would have cause foo to throw
const x = Math.sqrt(val); 

Function overloads will do this. For example,

function foo(val: string): void;
function foo(val: number): number;
function foo(val: string|number) {
  if (typeof(val) !== 'number') {
    throw new Error('val must be a number');
  }
  return Math.sqrt(val);
}

Math.sqrt(foo(123)); // ok
Math.sqrt(foo('asdf')); // not ok

See on the Typescript Playground .

If you've got a void -returning function which has the effect of narrowing the type of one of its parameters, and if you are running TypeScript 3.7 or above, you can make it an assertion function and the compiler will take that assertion into account. An assertion function's return type starts with the asserts modifier and is followed by the condition we'd like the function to assert. This condition can either be the name of one of the function's inputs, or a type predicate that says one of the function's inputs is of a narrower type than its declared type. Here's how we'd do it with foo() :

function foo(val: string | number): asserts val is number {
    if (typeof (val) !== 'number') {
        throw new Error('val must be a number');
    }
    // do something with number
}

So we've changed void to asserts val is number . If the function does return, the type of val will be narrowed from string | number string | number to number , as desired:

const val: string | number = Math.random() < 0.5 ? "str" : 123;

foo(val);

const x = Math.sqrt(val); // okay

Note that assertion functions only work on void -returning functions, so you can't have a function that acts both as a type assertion and returns some value. And there are other caveats (such as they require explicit annotations to be called ). But your question is so perfectly suited to assertion functions that if I didn't know better I'd think this was asked by one of the language designers in order to showcase the power of assertion functions!

Okay, hope that helps; good luck!

Playground link to code

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