Repository navigation
Partial<T>[K] is not assignable to T[K] | undefined #47523
Description
Activity
Possibly this is due to #30769?
RyanCavanaugh commented
on Jan 20, 2022 MemberMore actionsIndexed access on a mapped type stays deferred, by design (since this improves the specificness of many constructs). The only way to know that
{ [K in keyof T]: T[K] | undefined }[K]is assignable toT[K] | undefinedis to know that at a higher level this mapped type doesn't do something else like{ [K in keyof T]: T[K] | string | undefined }[K]. We can do some reaching backwards but it's not a generalizable principle, and pushes the inconsistency further down the line rather than remove it.An equivalent[ish] definition that works well is
type Foo = { x: string, y: number }; function getValueConcrete<P extends Partial<Foo>, K extends (keyof Foo) & (keyof P)>(o: P, k: K): P[K] | undefined { return o[k]; }
this has the advantage that nonsense reads like
getValueConcrete({x: "" }, "y");
fail
- addedAwaiting More FeedbackThis means we'd like to hear from more people who would be helped by this featureThis means we'd like to hear from more people who would be helped by this featureSuggestionAn idea for TypeScriptAn idea for TypeScript
on Jan 20, 2022 Does that mean the generic example doesn't report an error is because TypeScript didn't check it (deferred)?
The error message is cryptic:
Type 'Partial<Foo>[K]' is not assignable to type 'Foo[K] | undefined'. Type 'string | number | undefined' is not assignable to type 'Foo[K] | undefined'. Type 'string' is not assignable to type 'Foo[K] | undefined'. Type 'Partial<Foo>[K]' is not assignable to type 'Foo[K]'. Type 'number | undefined' is not assignable to type 'number'. Type 'undefined' is not assignable to type 'number'.
This usage looks very common to me, for example I may write:
type FooOptions = { x: number, y: string, }; class Foo { constructor(private options: Partial<FooOptions>) { } getOption<K extends keyof FooOptions>(key: K): FooOptions[K] | undefined { return this.options[key]; // Error } }
- addedFix AvailableA PR has been opened for this issueA PR has been opened for this issue
on Mar 6, 2023
Bug Report
...ok, I lied a little:
Tis a type variable / generic; butTis concrete.So this report can be seen either as the lack of support of an expected rule (when
Tis concrete), or an inconsistency between the rules for generics and the rules for concretes.🔎 Search Terms
Searching for:
I found these potentially related issues:
undefinedfromPartial<T>[keyof T] | undefined#45257 (comment)#45257 looks very similar. However, I think this report is a simpler case, and seems more directly related to the definition of
Partial<>/ optional properties (?), as something that adds| undefinedto the type of a property access expression. It also looks to me like a fix for this report would fix #45257 naturally.#31675 also looks related, but from the non-nullable perspective rather than the nullable one. The non-nullable inference rule also appears to be missing from both generic inference and concrete inference (e.g.,
NonNullable<Partial<T>[K]>is not assignable forT[K]for both genericTand concreteT), so at least there isn't an inconsistency there.🕗 Version & Regression Information
This changed between versions 3.3.3 and 3.5.1.
In 3.3.3,
Partial<T>[K]was assignable toT[K] | undefinedfor both genericTand concreteT.From 3.5.1, assignability is still allowed for generic
T, but not for any particular concreteT.⏯ Playground Link
Playground link with relevant code
💻 Code
🙁 Actual behavior
Partial<T>[K]is assignable toT[K] | undefinedwhenTis a genericPartial<T>[K]is not assignable toT[K] | undefinedwhenTis concrete🙂 Expected behavior
Partial<T>[K]toT[K] | undefinedshould be consistent for both genericTand concreteT