【问题标题】:Can I get a list of field names of an interface whose types are of a given type?我可以获得类型为给定类型的接口的字段名称列表吗?
【发布时间】:2021-03-29 01:46:09
【问题描述】:

比如如果我有这个接口

interface Measurement {
  date: Date
  width: number
  height: number
  depth: number
  comment: string
}

有没有办法让我获得以下内容?

const numericFields = ['width', 'height', 'depth']
const dateFields = ['date']
const stringFields = ['comment']

或者,有没有一种方法可以创建如下函数?

// not sure about the signature, but hopefully you get the idea
function getInterfaceFieldsWithType<I, T>: keyof I[]

const numericFields = getInterfaceFieldsWithType<Measurement, number>()

【问题讨论】:

    标签: typescript types typescript-generics


    【解决方案1】:

    第一个非常幼稚的方法:

    interface Measurement {
      date: Date
      width: number
      height: number
      depth: number
      comment: string
    }
    
    type GetByType<T, V> = {
      [P in keyof T]: T[P] extends V ? P : never
    }[keyof T]
    
    /**
     * Just an alias to reduce repetitive code
     */
    type Get<T> = Array<GetByType<Measurement, T>>
    
    const numericFields: Get<number> = ['width', 'height', 'depth']
    const dateFields: Get<Date> = ['date']
    const stringFields: Get<string> = ['comment']
    
    

    缺点:

    const numericFields: Get<number> = ['width', 'height', 'depth', 'depth', 'width'] // valid from TS perpective
    

    如果你想禁止TS复制,可以试试this answer

    如果你想100%确定运行时没有重复,最好的方法是使用Set's

    const array = [...new Set([1, 2, 3, 4, 4])] // [1, 2, 3, 4]
    

    第二种方法

    interface Measurement {
      date: Date
      width: number
      height: number
      depth: number
      comment: string
    }
    
    // filter all properies
    type GetByType<T, V> = {
      [P in keyof T]: T[P] extends V ? P : never
    }[keyof T]
    
    /**
     * Just an alias to reduce repetitive code
     */
    type Result = GetByType<Measurement, string>
    type Values<T> = T[keyof T]
    
    
    /**
     * because our second argument is stringified type name,
     * we need some sort of vice-versa mapping 
     */
    type LiteralToType<T> = T extends 'string' ? string : T extends 'number' ? number : T extends 'Date' ? Date : never;
    type TypeToLiteral<T> = T extends string ? 'string' : T extends number ? 'number' : T extends Date ? 'Date' : never;
    
    const getInterfaceFieldsWithType = <
      Obj extends Record<string, unknown>,
      Type extends TypeToLiteral<Values<Obj>> // I need a guarantee that we have at least one property with expected type
    >(obj: Obj, expectedType: Type): GetByType<Obj, LiteralToType<Type>>[] => {
      const keys = (Object.keys(obj) as Array<keyof Obj>)
      /**
       * Here, filter is a typescript guard
       * It says: if I return true, then you can sleep well, this key is what you need
       */
      return keys.filter((elem): elem is GetByType<Obj, LiteralToType<Type>> => typeof obj[elem] === expectedType)
    }
    
    /**
     * Tests
     */
    
    const result = getInterfaceFieldsWithType({ date: '01-01-2021', width: 24, height: 42, numberdepth: 2, comment: 'hello' }, 'number') // ok ("width" | "height" | "numberdepth")[]
    const result2 = getInterfaceFieldsWithType({ date: new Date(), width: 24, height: 42, numberdepth: 2, comment: 'hello' }, 'Date') // ok "date"[]
    const result3 = getInterfaceFieldsWithType({ date: new Date(), width: 24, height: 42, numberdepth: 2, comment: 'hello' }, 'string') // ok "comment"[]
    const result4 = getInterfaceFieldsWithType({ date: new Date(), width: 24, height: 42, numberdepth: 2, comment: 'hello' }, 'Promise') // error
    

    如果你仍然想允许不存在的属性类型,比如Promise,你可以在你的函数中添加下一个重载

    type EmptyArray = readonly string[];
    
    interface Overloading {
      <
        Obj extends Record<string, unknown>,
        Type extends TypeToLiteral<Values<Obj>> //I need a guarantee that we have at least one property with expected type
        >(obj: Obj, expectedType: Type): ReadonlyArray<GetByType<Obj, LiteralToType<Type>>>
      <
        Obj extends Record<string, unknown>,
        Type extends string // I need a guarantee that we have at least one property with expected type
        >(obj: Obj, expectedType: Type): EmptyArray
    }
    

    【讨论】:

      猜你喜欢
      • 2017-04-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-20
      相关资源
      最近更新 更多