What I'm trying to do is this:
interface A {
a: number
b: number
}
function f<T extends A>() {
const x: Partial<Record<keyof T, string>> = {a: 'generz'}
console.log(x)
}
But, when compiling (using tsc v4.9.3
) I get this error message:
error TS2322: Type '{ a: "generz"; }' is not assignable to type 'Partial<Record<keyof T, string>>'.
I don't understand why and I would like to have an explanation on this error. If T extends A
then keyof T
is a superset of keyof A
(containing at least 'a'|'b'
), so the {a: 'generz'}
would be legal independently of T
? Am i missing something?
I've found out, that creating a custom PartialRecord
type (described here) like this:
type PartialRecord<K extends keyof any, T> = {
[P in K]?: T
}
And then changing the type of variable x
to PartialRecord<keyof T, string>
, like this:
const x: PartialRecord<keyof T, string> = {a: 'generz'}
Compiles the code without complaining.
This would be equivalent code and it compiles too:
const x: Partial<Record<keyof T, string>> = {}
x.a = 'generz'
Although is not what i'll like to do.
What I'm asking is NOT "how to make it work in alternative ways", but is "why it doesn't work that way", since I expect it to work. Otherwise I'd just do {a: 'generz'} as Partial<Record<keyof T, string>>
(or worse as any
).
And if it wasn't clear: the code is nothing more than the minimum necessary to reproduce the error. So its purpose is not to make sense or do anything useful.
Nesting a Record<keyof T, string>
inside a Partial
leads to a level of abstraction which can not be properly understood by the compiler. Both Record
and Partial
are implemented as mapped types which can not be fully resolved when a yet unspecified generic type is supplied.
While the logic behind your assignment might be sound, it requires higher level reasoning by the compiler about the implications of nesting Record<keyof T, string>
inside a Partial
. Yet, it seems to be inable reason about this type at all leaving it essentially opaque. No expression would identify this type as a valid assignment target leaving only a type assertion as a workaround.
There have been multiple open issues about these higher order type problems like #43846 where Partial
being wrapped around Omit
lead to the same error or #28884 where an intersection of
Pick<T, Exclude<keyof T, K>> & Pick<T, Extract<keyof T, K>>
was not assignable to T
. The compiler would fail to properly evalaute the generic types leaving them opaque, even though these operations a reasonable and mostly sound to us.