-
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
undefined extends all types when accessing a generic in a mapped typed #27470
Comments
The issue seems to be the constraint on type UndefinedKeys<T> = {
[K in keyof T]: undefined extends T[K] ? K : never
}; The index access expressions I think this is a minimal repro (using arrays). type X<T extends any> = undefined extends T ? true : false;
type Y<T extends any[]> = undefined extends T[number] ? true : false;
type A = X<number>; // ignores the `any` constraint and returns false
type B = Y<number[]>; // returns true; The fundamental issue (I think) is that arrays and objects are covariant, which is unsound for update operations. EDIT: Alternate implementation with constraint: type UndefinedKeys<T extends Record<string, any>> = {
[K in keyof T]: undefined extends Extract<T[K], undefined> ? K : never;
}; |
This one is rather involved. We currently have the following rule:
This rule is unsound because Now, in 3.1 we fixed several issues relating to us not examining all constraints of a type. Because of those fixes we now drill all the way down and find the function test<T extends Record<string, any>, K extends keyof T>(t: T, k: K) {
t[k] = 42;
t[k] = "hello";
t[k] = undefined;
} That's not good. But the question is how to fix this without breaking the So far I think the best contender is to modify the rule above as follows:
In other words, we should limit the unsoundness to situations where one or the other part of the indexed access is an actual zero-order type, but forbid it when both are higher order (since at that point it becomes too unsound). I ran the experiment on our test suites and there just a few changes, all of which are reasonable. Even better, the RWC suites show no baseline changes at all. I will put this up in a PR. |
FWIW I don't think it is because type-checking is turned off, but because type UndefinedKeys<T extends Record<string, unknown>> = {
[K in keyof T]: undefined extends T[K] ? K : never
};
type MyType = {a: string, b: string | undefined}
type Result1 = UndefinedKeys<MyType>;
const a1: Result1['a'] = 'a'; // no error The conditional type is essentially a type-level version of this: const x: {a: string} = { a: "a" };
const baseConstraint: Record<string, unknown> = x;
baseConstraint.a = undefined; // unsound
Am I right in say that this would not address:
(This isn't a complaint, just wanting to know if I read the rule ok).
From an uninformed/outside perspective it seems like the problem trying to be solved in the initial post is to constrain I wonder whether it would be possible to have a special way of marking shapes for types where you really don't care about, or particularly, want their constituent constraints. Something like: type UndefinedKeys<T extends Record<string, *>> = {
[K in keyof T]: undefined extends T[K] ? K : never
};
|
That is indeed what I meant.
I would be more inclined to introduce a new type relationship (e.g. "soundly assignable") that excludes the unsound rule and then use that relationship when checking the |
Is this related? playground Using "brackets on a mapped type" inside an "extends" clause lets |
@aleclarson Yes this seems to be related. The fix made only works for index accesses of the form
This isn't 100% true in the sense that what is going on is that the condition |
TypeScript Version:
3.2.0-dev.20180929
(First noticed in3.1.1
)strictNullChecks
must be on otherwise this is expected behaviour.It seems that an issue has been introduced when accessing attributes of a mapped type in a conditional type.
Search Terms:
conditional type undefined extends
mapped type undefined extends
conditional type in mapped type access
Code
To simplify this even more line 5 can be changed to
Expected behavior:
Result1
should be{a: never; b: 'b'}
.Type '"a"' is not assignable to type 'never'.
Actual behavior:
Result1
is{a: 'a'; b: 'b'}
.Playground Link:
strictNullChecks
IN OPTIONSRelated Issues:
Potentially: #26942
Initially thought it was an issue with Distributive conditional types but the Generic type is in the assignable to position.
The text was updated successfully, but these errors were encountered: