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.

Interaction of empty arrays and `satisfies` is unsatisfying

See original GitHub issue


🔍 Search Terms

List of keywords you searched for before creating this issue. Write them down here so that others can find this suggestion more easily and help provide feedback.

✅ Viability 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, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript’s Design Goals.

⭐ Suggestion

when we use satisfies keywords, infer empty array to typed array by satisfies target array instead of never array

📃 Motivating Example

interface TreeNode {
  children: TreeNode[];

const a = {
  children: [],
} satisfies TreeNode;

// expect: TreeNode[]
// accept: never[]
// ^?

💻 Use Cases

infer a.children to TreeNode[]

Issue Analytics

  • State:open
  • Created 9 months ago
  • Reactions:1
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

RyanCavanaughcommented, Dec 13, 2022

Why is the default inference for [] (in the absence of a conditional type) not unknown[]? It feels weird to infer never[] …

Good question and a lot to unpack here. I’ll speak only in terms of today’s behavior for clarity. Empty arrays are (again, currently) not affected by contextual typing except that they can form 0-length tuples in the presence of a tuple-like contextual type. So if you look at a call like

declare function doSomething(blah: number[]): void

Here the expression [] has type never[] and the call succeeds. Had [] had type unknown[], the call would have failed.

For types given to inferred variables, the situation is much tricker due to evolving arrays. But object properties don’t qualify for evolving arrays, so the situation is analogous:

const obj = { a: [] }
declare function doSomething(blah: { a: readonly number[] }): void

This call is processed the same way: obj.a is never[]. This one is probably the most important to get right since there is no contextual typing to “fix” the type of [] in this example.

likely intends to push stuff into it afterwards

Sometimes yes, sometimes no. There are a lot of APIs like

sendEmail(subject, body, /*attachments*/ []);

where it makes sense to just give someone a fresh empty array literal that doesn’t have any bindings pointing to it. The “sometimes yes” case is indeed why we went to the trouble to make evolving arrays (those whose type is inferred via CFA looking for calls to push/etc), since in the case where you start with a variable initialized with [], almost certainly it’s not going to stay empty.

TL;DR handling both covariant and contravariant inference is hard

jcalzcommented, Dec 11, 2022

The functionality of satisfies, as implemented in #46827, was discussed in #7481 and then #47920, and it was decided that the “safe upcast” functionality couldn’t really be directly supported while also supporting all the other use cases. In this comment it was shown that to get a safe upcast you could write x satisfies T as T which, while annoying, is at least possible:

const a = {
  children: [],
} satisfies TreeNode as TreeNode; // safe upcast

But if you’re already assigning to a variable you might as well annotate:

const a: TreeNode = { // annotate
  children: [],

Or, depending on what you’re trying to do:

const a = {
  children: [] satisfies TreeNode[] as TreeNode[] // safe upcast here
} satisfies TreeNode;

I think it would be wonderful to have a specific operator for safe upcasting (shorten x satisfies T as T to x satisfiesas T / x satisfas T / x sassafras T / x sass T / x ass T), like an in-line type annotation. (Is there an open issue for this?) But I don’t think satisfies alone will ever do this for you.

(Note: for empty arrays in particular it would lovely if nothing were ever inferred as never[], but I don’t know that they’d want to special case how empty arrays behave with satisfies.)

Read more comments on GitHub >

github_iconTop Results From Across the Web

JavaScript fails in computing arrays when user interaction is ...
JavaScript fails in computing arrays when user interaction is very fast, could it just be me?
Read more >
Why does Array.prototype.every return true on an empty array?
In particular, for an empty array, it returns true. (It is vacuously true that all elements of the empty set satisfy any given...
Read more >
A Unit Testing Practitioner's Guide to Everyday Mockito - Toptal
RETURNS_MOCKS first tries to return ordinary “empty” values, then mocks, if possible, and null otherwise. The criteria of emptiness differs a bit from...
Read more >
Locating Arrays: Construction, Analysis, and Robustness - KEEP
are satisfied and bad events are requirements that are violated. A collection of tests is a locating array when no bad events occur....
Read more >
JS Arrays: To Be Empty, Or Not To Be ... - Mayumi Nishimoto
The fact that not all properties in an array are elements have interesting consequences. For example, the array can include keyed collection and...
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 Post

No results found

github_iconTop Related Hashnode Post

No results found