-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Generic mapped type assignability rules don't check modifiers type properly #27598
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
In homomorphic mapped types of one of the forms |
@ahejlsberg I understand. The problem is that the assignability rules are inconsistent with actual mapped type instantiation in the way they check for the special forms |
I have hit a similar issue. However I am not sure whether it is the same problem or not. If not, I will open another issue. // my code
type FieldKind = "numeric" | "text"
interface FieldMeta {
kind: FieldKind
}
type FieldsMeta<T> = { [K in keyof T]-?: FieldMeta }
type Compoment = "aComponent"
type FieldComponents<T> = { [K in keyof T]-?: Compoment }
function createComponentsFromMeta<T>(meta: FieldsMeta<T>): FieldComponents<T> {
return mapValues(meta, (value, key) => "aComponent" as Compoment) // <-- type error here
}
// external lib
declare type ObjectKey = string | number | symbol
declare type ObjectMap<K extends ObjectKey, V> = { [P in K]: V };
declare type MapObject<K, I, O> = (value: I, key: K) => O;
// something like lodash.mapValues
declare function mapValues<K extends ObjectKey, I, O>(obj: ObjectMap<K, I>, map: MapObject<K, I, O>): ObjectMap<K, O> This will fail with 'ObjectMap<keyof T, "aComponent">' is not assignable to type 'FieldComponents'. interface Foo {
a: number
b?: string
}
const fooMeta: FieldsMeta<Foo> = {
a: { kind: "numeric" },
b: { kind: "text" }
} |
@n9 Your case is different from the cases in my original report (your case involves assignability between two generic mapped types, while mine involve assignability between a generic mapped type and another type), but it still falls under the summary of "Generic mapped type assignability rules don't check modifiers type properly", so I am happy to consider it included in this issue. Now we just hope for the issue to be unmarked as "Working as intended"! A workaround is to change the definition of type FieldComponents<T> = Record<keyof T, Compoment>; Then instead of using |
@mattmccutchen Great, thanks for the workaround! To be honest, I've just monkey-copied your pattern. But still I do not understand why you pattern works and does not require |
@n9 Roughly. This is one of TypeScript's many underdocumented rules that tries (and sometimes fails) to do what you want. The decision of the "modifiers type" (if any) from which modifiers will be copied is based on the original definition of the mapped type, before any type parameters are instantiated. When you write |
@mattmccutchen Thank you for your explanation. These modifiers look really great at first sight. However it seems to me now, that they are rather fragile. So there is difference between @ahejlsberg, @RyanCavanaugh In case this is |
I agree that the determination of whether a mapped type is homomorphic is fragile and surprising, but I wouldn't expect it to be changed at this point. But that's not what this issue is about. This issue is about the assignability rules being inconsistent with the way homomorphic mapped types are determined. ahejlsberg's comment accompanying the "working as intended" label does not appear to address the cases in my original report, so I believe he just didn't read the issue carefully enough. |
@mattmccutchen Am I correct that the issue you're referring to is this? type Pick1<A, B extends keyof any> = {[P in keyof A & B]: A[P]};
type Pick2<A, B extends keyof any> = {[P in keyof A & B]-?: A[P]};
function foo<T>(x: T, y: Pick1<T, keyof T>, z: Pick2<T, keyof T>) {
y = x; // Ok
z = x; // Error
} In other words, since |
@ahejlsberg There are lots of cases in which the existing assignability rules for generic mapped types are inconsistent with the way generic mapped types are instantiated, and I was hoping that this issue would cover all of the inconsistencies we can find. (The issue by itself is low priority unless/until realistic code is affected by the problems; I filed it because I thought if we planned to redo the assignability rules anyway for #27484, we should try to get all of these cases fixed at the same time.) Your example is equivalent to the |
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
More warmup for #27484.
TypeScript Version: master (85a3475)
Search Terms: generic mapped type assignable assignability relation optional modifiers type
Code
Expected behavior: All three
WeirdPick
constructions give errors.Actual behavior: None of the
WeirdPick
constructions give errors.Playground Link: link
Related Issues: none found
The text was updated successfully, but these errors were encountered: