-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Object type unions inside interfaces result in indexed access being too complex #35216
Comments
Ah, and that's where you're wrong. It's not the same, it's much stricter. Notably, when assigning to a |
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
I'm a bit confused, why do you need to be able to write to all of the possible values, not just one value? If I read from Let's take an example of a somewhat standard Redux store but with some very un-Redux mutable getters and setters as they illustrate the issues better. Let's say I have a store with three things in it. interface Store {
counter: number;
todo: string;
done: boolean;
};
const store: Store = {
counter: 123,
todo: 'get bread',
done: false,
} Then I can define a generic getter and setter for this, and everything seems work fine. function set<T extends keyof Store>(type: T, value: Store[T]) {
store[type] = value; // This works as I would have expected
}
function get<T extends keyof Store>(type: T) {
return store[type]
} If you look at the return values of const counter = get('counter'); // This is of type number
const todo = get('todo'); // This is of type string
const done = get('done'); // This is of type boolean Similarly calling the setters works as expected, accepting values of the right types and not the wrong ones. set('counter', 42); // Works
set('done', 1); // Argument of type '1' is not assignable to parameter of type 'boolean'.ts(2345)
set('done', true); // Works Looking at the types, they are what I would have expected. type Done = Store['done']; // This is `boolean` The intersection of these types in type All = Store['counter'] & Store['todo'] & Store['done']; // never But it seems like indeed the actual type function set<T extends keyof Store>(type: T, value: Store[T]) {
store[type] = 1; // This returns an error.
} this gives an error
So it seems the only reason Interestingly, if inside let foo: Store[T];
foo = 1; I get the error let bar: Store['counter' | 'todo' | 'done'];
bar = 1; then this works. Which I think shows I'm misunderstanding something. I had assumed previously that my issues with needing to occasionally cast via function test(unknownType: 'counter' | 'todo') {
const unknownValue = get(unknownType); // type: number | string
} but it seems like this is not a reason. Can you explain why it is that you need to be able to assign to all not any? This seems to restrict the ability to nicely type a Redux store. The fact that the |
When you read a value of type |
Does this mean that code like function set<T extends keyof Store>(type: T, value: Store[T]) {
store[type] = value;
}
function get<T extends keyof Store>(type: T) {
return store[type]
} is wrong and should not be expected to compile? Is there a better way to be writing code like this? |
Nope, those are fine (ish) - The issue in your original example is actually To see how this affects your code in a concrete way, take your example from the OP and add: type NestedUnionOne = "a" | undefined;
type NestedUnionTwo = "b" | undefined;
type NestedUnionThree = "c" | undefined;
type NestedUnionFour = "d" | undefined;
type NestedUnionFive = "e" | undefined; (and make sure you're in |
Thanks for your really detailed responses. I always appreciate the work you and your team do, fixing issues and taking the time to provide really helpful responses. The unification to We hit a new issue with this approach with unification so I've made a new issue #36245. |
Postscript: This behaviour happens only when strictNullChecks is on. |
TypeScript Version: 3.7.2 (anything > 3.5)
Search Terms: indexed access type union too complex to represent
Code
Expected behavior:
Compiles
Actual behavior:
Inside
maybeItem
, the return line errors withExpression produces a union type that is too complex to represent.
Things that do make it compile:
NestedUnionOne
as every type in CollectionsThings that don't make it compile:
NestedUnioneOne
,NestedUnionTwo
etc all the same definitionIt looks like the compiler is exploding the union out to 10^5 instead of 10*5 and "ignoring" it because it's over the 100k limit. Not sure if there's a solution in that case.
For some context, this is for a document store, where
Collections
represents the types of all the possible documents. Some of the documents are (usually discriminated) union types, such as notifications. ThemaybeItem
example is convoluted but a more realistic example would begetItem<K keyof Collections>(collection: K, id: number) => Collections[K] | undefined
.Playground Link: Playground link
The text was updated successfully, but these errors were encountered: