question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Infer arrow function type guard type for specific simple cases

See original GitHub issue

Search Terms

arrow function type guard

Suggestion

Specific simple cases of arrow functions of a single argument should be considered type guards:

const f1 = x => x typeof "string"; // immediately returns a typeof expression with a literal rhs
const f2 = x => x instanceof Foo; // immediately returns an instanceof expression
const f3 = x => x === "foo"; // immediately returns a strict (non-)equality comparison with a literal
const f4 = x => x == null; // immediately returns a non-strict (non-)equality comparison with a null literal
const f5 = x => isFoo(x); // immediately returns another type guard

There are more general requests like https://github.com/microsoft/TypeScript/issues/16069, but I beleive this one should be easier to implement, and it still covers a lot of use cases.

Use Cases

filter with heterogenous arrrays:

// currently: (string | number)[]
// would be: string[]
const a = [1, "foo", 2, "bar"].filter(x => x instanceof "string");

Specifically, filtering out null or undefined elements:

// f: (x: string) => Promise<string | undefined>
const a = await Promise.all(["foo", "bar"].map(f));
const b = a.filter(x => x !== undefined);

Limiting the number of arguments of a more complex type guard:

// isFoo: (x: any, strict?: boolean) => x is Foo
const b1 = a.filter(x => isFoo(x));
const b2 = a.filter(x => isFoo(x, true));

// compare:
const b3 = a.filter(isFoo); // No overload matches this call.

Examples

Checklist

My suggestion meets these guidelines:

  • This wouldn’t be a breaking change in existing TypeScript/JavaScript code
  • This wouldn’t change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn’t a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript’s Design Goals.

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:75
  • Comments:12 (3 by maintainers)

github_iconTop GitHub Comments

12reactions
PMLP-novocommented, Aug 23, 2022

I know that this is blocked by introducing breaking changes.

But has anybody a real world example of production code, that would break? It seems like a close to unreachable corner case.

Secondly if somebody where to reach this corner case they could very quickly resolve it and would notice it right away when they upgrade typescript.

This question has been asked so many times and so many people spend time discussing it and having a bad typescript experience.

So I argue that this is a case where the right solution is to introduce a breaking change.

8reactions
acutmorecommented, Nov 18, 2020

Personally I don’t feel the desire for type hint syntax.

A new --stictXXXX flag added seems inline with other breaking changes to TS’s type inference. It would be interesting if someone could produce some stats on the number of times this would trigger a breaking change when sampled on a group of open-sources repos, also if the error actually seemed to catch a potential bug.

Anecdotally I have come across code like this a few times:

declare let someArray: (string | null)[];
declare function useString(s: string): void;

someArray
  .filter(v => v !== null)
  .forEach(v => {
     useString(v!); // unnessesary null assertion to stop strictNullError
  });

With added syntax required I can see people still being inclined to continue to just add the null assertion, this new ‘infer type guard’ syntax will possibly be seen as ‘advanced syntax’ that not everyone is aware of.

When code is written in a structured style TS infers the type guard without syntax:

for (const v of someArray) {
    if (v !== null) {
        useString(v); // value is inferred correctly without type syntax
    }
}

This all said, I do feel that the infererence should only be done for inline callbacks where the function lists a type guard as one of the possible parameters. Otherwise I could see it being a performance issue on large code bases.

// no type guard infered
const f = (v: any) => typeof v === 'string;

// type guard infered because of filter's type being:
//   filter<S extends T>(cb: (value: T) => value is S): S[];
arr.filter(v => typeof v === 'string) 
Read more comments on GitHub >

github_iconTop Results From Across the Web

Type guards and assertion functions • Tackling TypeScript
As we have seen, a type guard is an operation that returns either true or false – depending on whether its operand meets...
Read more >
Typescript Type Guards and fat arrow function - Stack Overflow
This is a design limitation. A way to work around this issue is to assign g to a new variable, which will have...
Read more >
Functions - TypeScript: Handbook
Writing the function type​​ We make it clear which is the return type by using an arrow ( => ) between the parameters...
Read more >
How to use type guards in TypeScript - LogRocket Blog
Type guards are regular functions that return a boolean, taking a type and telling TypeScript if it can be narrowed down to something...
Read more >
TypeScript Fundamentals - Joy of Code
Type Assertion Conversion; Literal Types; Literal Inference; Object Index Signatures; Type Narrowing; Type Guards; Type Predicates; Generics ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found