-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Interaction of empty arrays and satisfies
is unsatisfying
#51853
Comments
The functionality of 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 (Note: for empty arrays in particular it would lovely if nothing were ever inferred as |
I believe @RyanCavanaugh is on record as saying that nothing should ever be inferred as edit: Found it. #47531 (comment) |
It's probably time to revive #47898 |
satisfies
keywordsatisfies
is unsatisfying
Some notes from discussion and post-discussion thinking "Copying" the contextual type is just wrong (the first revision of the PR didn't do this either) because you might have Which element types, though? You might write const m = [] satisfies ArrayLike<number> where the desired type is presumably For unions, things get tricky. It's tempting to say that for an offered type // Should pass, but would fail if [] has type (string | number)[]
const m: string[] | number[] = [] satisfies string[] | number[] type Obj = { a: string[] } | { a: number[] };
// Should pass, but would fail if [] has type string[] | number[]
const m: Obj = { a: [] } satisfies Obj; These examples are illustrative -- the only thing that works here is to give the I'll update the PR to implement the behavior that we give an empty array a type of the intersection of its contextual types' element types if they are ArrayLike |
Why is the default inference for |
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
doSomething([]); Here the expression 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
doSomething(obj); This call is processed the same way:
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 TL;DR handling both covariant and contravariant inference is hard |
@RyanCavanaugh I hit upon this issue yesterday in a case where I want to access an array property of an object where only some of the possible properties are defined: type Example = {
a?: string[] | null;
aa?: string[] | null;
}
const example = {
a: [],
} satisfies Example;
const { a } = example;
// Argument of type 'string' is not assignable to parameter of type 'never'.(2345)
a.push('');
type NonNullableFields<T> = {
[P in keyof T]: NonNullable<T[P]>;
};
const workaround: NonNullableFields<Required<Example>> = {
a: [],
aa: [],
}
const { a: b } = workaround;
b.push(''); The safe upcast pattern doesn't work here, only workaround I found was to define all the properties and make a |
Suggestion
π 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:
β Suggestion
when we use
satisfies
keywords, infer empty array to typed array bysatisfies
target array instead of never arrayπ Motivating Example
https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgCpQhAcgewCYoDeAUMsggBbAA2eGIAXGhtvhANoC6A3MQL7FiCHCADOYZHGQBeZCTKUadCI2RcANP2Si4YYKJjAIo5plwFexAPRXkEAB4AHCAjBN0Ztl2JwAdItp6a1sAPQB+YiA
π» Use Cases
infer
a.children
toTreeNode[]
The text was updated successfully, but these errors were encountered: