如何使用接口中的 属性 作为索引类型

How to use a property from an interface as an index type

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 */) {}
}

有人知道如何获取In["name"]的typeof作为Mapping的索引类型吗?所以我不必写 "x" 两次

我已经试过了,不知何故 prop 变成了任何

interface In {
  readonly name: keyof Mapping,
  fn: Mapping[In["name"]]
}
const inHandlers: In<"x"> = {
  name: "x",
  fn(prop /* :any*/) {}
}

您可以使用恒等函数来约束参数类型:

TS Playground

function createMapping <T extends keyof Mapping>(mapping: In<T>): In<T> {
  return mapping;
}

const xMapping = createMapping({
  name: "x",
  fn (prop) {} // prop is string
}); // xMapping is In<"x">

const yMapping = createMapping({
  name: "y",
  fn (prop) {} // prop is number
}); // yMapping is In<"y">

如果你预先知道 Mapping 结构,你就可以做到。

首先你需要使用mapped types创建所有possible/allowed数据结构:

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>

您可能已经注意到,我们最终得到了嵌套对象,其中的值表示允许的状态。现在,我们需要以某种方式获得允许值的并集,或者换句话说,使 discriminated union type 考虑一下:

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>>

我已将结果包装在 Values 实用程序类型中。这种类型 returns 没有键的所有对象值的联合。这其实就是我们想要的。 我们也可以重构一下:


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 */) { }
}

Playground

您不需要使用任何额外的通用参数。 Here you can find similar question and here你可以找到我的文章