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.