【问题标题】:How to use a property from an interface as an index type如何将接口中的属性用作索引类型
【发布时间】:2022-01-02 06:02:59
【问题描述】:
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"] 的类型作为Mapping 的索引类型吗?这样我就不用写两次"x"

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

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

【问题讨论】:

    标签: typescript indexing types


    【解决方案1】:

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

    首先您需要使用mapped types 来创建所有可能/允许的数据结构:

    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 实用程序类型中。此类型返回没有键的所有对象值的联合。这实际上是我们想要的。 我们也可以稍微重构一下:

    
    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你可以找到类似的问题,here你可以找到我的文章。

    【讨论】:

    • 非常有帮助?它工作但有没有办法减去Dictionary[Prop]Parameters&lt;&gt;?我想覆盖第一个参数并使用给定函数Dictionary[Prop]的其余参数(基本上就像绑定一个函数类型)
    • @LeVal 类似this ?
    【解决方案2】:

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

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

    【讨论】:

    • 没有别的办法吗?喜欢而不是写&lt;"key"&gt;name: "key"
    • 很遗憾,不,您不能从尚未定义的对象中提取类型信息以限制其自身的形状。
    • 可以从Mapping制作discriminated union type
    • @captain-yossarian TIL 编译器实际上会推断出更具体的注解子类型!
    猜你喜欢
    • 2020-01-01
    • 2011-01-31
    • 2021-08-13
    • 2019-11-26
    • 1970-01-01
    • 2017-04-23
    • 1970-01-01
    • 1970-01-01
    • 2014-11-21
    相关资源
    最近更新 更多