-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Check field with optional chaining operator in union types #35542
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
Comments
While this definitely would be useful, I would expand it out to union access in more contexts if it were implemented. For example, following the same logic, the compiler could allow access to non-common members in conditionals and use it as a type guard : function someWork(owner: OneField | TwoField) {
if (owner.data) {
console.log(owner.data);
}
} One could go further and allow access in any context to non-common members by adding an Just one note, creating a type to transform a union in such a way to allow the code in the original example, is trivial, and the original union is even still compatible with this new type: type OneField = {
id: string;
}
type TwoField = {
id: string;
data: string;
}
type MakeAllUnionConstituentsHaveAllFieldsHelper<T, KAll extends PropertyKey> =
T extends T ? T & Partial<Record<Exclude<KAll, keyof T>, undefined>> :
never;
type AllKeys<T> = T extends T ? keyof T : never;
type MakeAllUnionConstituentsHaveAllFields<T> = MakeAllUnionConstituentsHaveAllFieldsHelper<T, AllKeys<T>>
function someWork(owner: MakeAllUnionConstituentsHaveAllFields<OneField | TwoField>) {
if (owner.data?.length) {
console.log(owner.data);
}
}
declare let o: OneField | TwoField;
someWork(o)/// Comaptible |
Duplicate of (the declined 😿) #33736 |
This is a correct error because the code is unsound; you have to account for the possibility of aliasing: type OneField = {
id: string;
}
type TwoField = {
id: string;
data: string;
}
function someWork(owner: OneField | TwoField) {
if (owner.data?.length) { // Property 'data' does not exist on type 'OneField | TwoField'.
// alleged to be OK
console.log(owner.data.toLowerCase());
}
// instead of
// if ("data" in owner && owner.data?.length) {
// console.log(owner.data);
// }
}
const obj = { id: "", data: 42 };
const alias: OneField = obj;
// Crashes
someWork(alias); |
@RyanCavanaugh Doesn't TypeScript have enough information there to know that These are the sorts of discussions Hegel.js is having. And I think it would be worthwhile to investigate such concepts (f.e. type checking based on usage sites, not just static information from the type definitions). Your previous example would do this: function someWork(owner: discriminated OneField | TwoField) {
if (owner.data?.length) {
// This is totally OK
console.log(owner.data.toLowerCase());
}
}
const obj = { id: "", data: 42 };
const alias = obj;
// Type error: alias is not assignable to owner because data is a number but owner expects string | undefined. or similar.
someWork(alias); Then we can simplify code in many places. Instead of // thing is type Foo | Bar, and only Bar has method()
if (foo instanceof Bar) {
foo.method()
} we can just write foo.method?.() A lot cleaner! Straying a little, maybe TypeScript can investigate a type Foo = discriminated OneField | TwoField
function foo(foo: discriminated OneField | TwoField) {} where Then the "smarter" type checker would see metadata for the example's Not sure if I'm using the correct wording, discriminated, but I think you get the idea. |
No, it doesn't. It only knows that |
TypeScript Version: 3.7.2
I'm seeing incorrect behavior when I trying to use optional chaining operator in union type. Instead of writing boilerplate code for using field with optional chaining operator I'm seeing use short construction. But...
Code
Search Terms
chaining operator, use field
Suggestion
Short construction for use field with optional chaining operator in union is available
The text was updated successfully, but these errors were encountered: