Issue
interface Mapping {
"x": (a: string) => void
"y": (b: number) => void
}
interface In<T extends keyof Mapping> {
readonly name: T,
fn: Mapping[T]
}
const inHandlers: In<"x"> = {
name: "x",
fn(prop /* :string */) {}
}
someone knows how to get the typeof In["name"] as the index type of Mapping? so I don't have to write "x" two times
I already tried and somehow prop becomes any
interface In {
readonly name: keyof Mapping,
fn: Mapping[In["name"]]
}
const inHandlers: In<"x"> = {
name: "x",
fn(prop /* :any*/) {}
}
Solution
If you know Mapping structure upfront, you can do it.
First of allm you need to use mapped types to create all possible/allowed data structures:
interface Mapping {
"x": (a: string) => void
"y": (b: number) => void
}
type Variants<Dictionary> = {
[Prop in keyof Dictionary]: {
name: Prop,
fn: Dictionary[Prop]
}
}
// type Result = {
// x: {
// name: "x";
// fn: (a: string) => void;
// };
// y: {
// name: "y";
// fn: (b: number) => void;
// };
// }
type Result = Variants<Mapping>
As you might have noticed, we are ended up with nested object where values represents allowed state. Now, we need to somehow obtain union of allowed values or in other words make a discriminated union type Consider this:
type Values<T> = T[keyof T]
interface Mapping {
"x": (a: string) => void
"y": (b: number) => void
}
type Variants<Dictionary> = {
[Prop in keyof Dictionary]: {
name: Prop,
fn: Dictionary[Prop]
}
}
// type Result = {
// name: "x";
// fn: (a: string) => void;
// } | {
// name: "y";
// fn: (b: number) => void;
// }
type Result = Values<Variants<Mapping>>
I have wrapped our result in Values utility type. This type returns a union of all object values without keys. This is actually what we want.
We also can refactor it a bit:
type Values<T> = T[keyof T]
interface Mapping {
"x": (a: string) => void
"y": (b: number) => void
}
type Variants<Dictionary> = Values<{
[Prop in keyof Dictionary]: {
name: Prop,
fn: Dictionary[Prop]
}
}>
type Handlers = Variants<Mapping>
const x: Handlers = {
name: "x",
fn(prop /* :string */) { }
}
const y: Handlers = {
name: "y",
fn(prop /* :number */) { }
}
You don't need to use any extra generic arguments. Here you can find similar question and here you can find my article.
Answered By - captain-yossarian
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.