Issue
The problem
I have the following two types.
interface SchemaItem {
name: string,
value: string
}
type Result<T extends SchemaItem[]> = {
[K in T[number] as K['name']]: K['value']
}
And I have a function that is supposed to create a value of the Result
type from the SchemaItem[]
argument.
function createResult<T extends SchemaItem[]>(schemaItems: T): Result<T> {
// implementation doesn't matter for this question
return {} as Result<T>
}
The Result
type works as expected when I test it like this.
// ResultType = { foo: "Foo", bar: "Bar" }
type ResultType = Result<[
{ name: 'foo', value: "Foo" },
{ name: 'bar', value: "Bar" },
]>
But when I test the function with the same data the returned value is not inferred properly.
// result is of type Result<{ name: string; value: string }[]>
const result = createResult([
{ name: 'foo', value: "Foo" },
{ name: 'bar', value: "Bar" },
])
Is it possible to make TypeScript infer the type of the result
as { foo: "Foo", bar: "Bar" }
or it's a design limitation?
What I have tried
I thought maybe the problem is that name
property of SchemaItem
is declared as string
and here is what I come up with.
interface SchemaItem<T extends string = string> {
name: T,
value: string
}
type ArrayToUnion<T> = T extends Array<infer R> ? R : never
type Result<T extends SchemaItem[]> = ArrayToUnion<T> extends SchemaItem<infer R> ? { [key in R]: string } : never
But it didn't help and the result
value was of type { [x: string]: string }
which is no better.
Solution
You can extend your generics from readonly
array types, and then supply a readonly array of object literal types to the function by using the as const
assertion on the input. (Using an as const
assertion tells the compiler to infer the type literally.)
interface SchemaItem {
name: string,
value: string
}
type Result<T extends readonly SchemaItem[]> = {
[K in T[number] as K['name']]: K['value']
}
type ResultType = Result<[
{ name: 'foo', value: "Foo" },
{ name: 'bar', value: "Bar" },
]>
function createResult<T extends readonly SchemaItem[]>(schemaItems: T): Result<T> {
return {} as Result<T>
}
const result = createResult([
{ name: 'foo', value: "Foo" },
{ name: 'bar', value: "Bar" },
] as const);
result.bar; // "Bar"
result.foo; // "Foo"
Answered By - jsejcksn
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.