-
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
How to use just one of interfaces with different properties #28879
Comments
I think this is a duplicate of #20863. Currently a property is excess in a union type if the property is excess in all branches. In this instance no one property is excess in both branch. I would be interested to get the opinion from the team as to whether it would be reasonable to have a partial solution that works for 1 or 2 levels deep (but correctly for intersection and unions). I think it would handle most cases people have, but would be tractable to implement and would hopefully retain the performance benefits EPC gives currently. I will also take this shameless opportunity to plug my experimental work on exact types, which would solve this issue (#28749). type Exact<T> = {| [K in keyof T]: T[K]; |}
type WithId = {
id: number
}
type WithNumber = {
number: number
}
type User = Exact<WithId> | Exact<WithNumber>;
const user: User = {
id: 1,
number: 1
};
/*
Type '{ id: number; number: number; }' is not assignable to type 'User'.
Type '{ id: number; number: number; }' is not assignable to type 'Exact<WithNumber>'.
Object literal may only specify known properties, and 'id' does not exist in type 'Exact<WithNumber>'. [2322]
*/ |
Probably worth noting that explicitly listing out all the properties you care about not being in excess with an optional undefined type will make the types work as you'd expect: type WithId = {
id: number
number?: undefined,
}
type WithNumber = {
id?: undefined,
number: number
}
type User = WithId | WithNumber
const user: User = {
id: 1,
number: 1
}; these are the kinds of types we infer when you're working with fresh literals to accomplish the same thing. But yes, mostly a duplicate of #20863 |
Here is a convoluted way to implement @weswigham's suggestion without changing the constituent types. (Disclaimer: I haven't really debugged these). type WithId = {
id: number;
}
type WithNumber = {
number: number;
}
const user: UserExact = { // error
id: 1,
number: 1
};
type User = WithId | WithNumber;
type UserExact = Exactify<User>;
type Exactify<T> = Exactify2<T, T>;
type Exactify2<T, U> = T extends unknown ? ExactifyWorker<T, U> : never;
type ExactifyWorker<T, U> = OptionalK<T, MinusKeys<T, U> & string>;
type KeyMap<U> = U extends unknown ? keyof U : never;
type MinusKeys<T, U> = Exclude<KeyMap<U>, keyof T>;
type OptionalK<T, K extends string> = T & { [P in K]?: undefined }; |
This issue has been marked as a duplicate and has seen no activity in the last day. It has been closed automatic house-keeping purposes. |
TypeScript Version: 3.3.0-dev.20181206
Search Terms: interface, union
Code
Expected behavior:
user
cannot have bothid
andnumber
propertiesActual behavior:
user
can have bothid
andnumber
properties. How can I achieve this?Playground Link: http://www.typescriptlang.org/play/#src=type%20WithId%20%3D%20%7B%0D%0A%20%20%20%20id%3A%20number%0D%0A%7D%0D%0A%0D%0Atype%20WithNumber%20%3D%20%7B%0D%0A%20%20%20%20number%3A%20number%0D%0A%7D%0D%0A%0D%0Atype%20User%20%3D%20WithId%20%7C%20WithNumber%0D%0A%20%20%0D%0Aconst%20user%3A%20User%20%3D%20%7B%0D%0A%20%20%20%20id%3A%201%2C%0D%0A%20%20%20%20number%3A%201%0D%0A%7D%3B
Related Issues: #12745, #20060, #23535
The text was updated successfully, but these errors were encountered: