简体   繁体   English

我可以获得类型为给定类型的接口的字段名称列表吗?

[英]Can I get a list of field names of an interface whose types are of a given type?

For example, if I have this interface例如,如果我有这个接口

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

Is there a way for me to get the following?有没有办法让我得到以下?

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

Alternatively, is there a way I can create a function like the following?或者,有没有一种方法可以创建 function,如下所示?

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

const numericFields = getInterfaceFieldsWithType<Measurement, number>()

First very naive approach:第一个非常幼稚的方法:

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

Drawbacks:缺点:

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

If you want to forbid dublications by TS, you can try this answer如果你想禁止 TS 复制,你可以试试这个答案

If you want to be 100% sure that there are no dublications in runtime, the best way is to use Set's :如果你想 100% 确定运行时没有重复,最好的方法是使用Set's

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

Second approach第二种方法

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

If you still want to allow non existence property types, like Promise , you can add next Overloading to your function如果您仍然希望允许不存在的属性类型,例如Promise ,您可以将下一个重载添加到您的 function

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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何获取多个联合接口类型的属性的值? - How can I get the value of a property of type multiple union interface types? 扩展@types - 从界面中删除字段,为界面中的字段添加类型 - Extending @types - delete field from interface, add type to field in interface 输入具有相同字段名称但不同类型的 - Type for with same field names but different types 如何将记录的值用作接口类型相同的 object 键 - How can I use a Record's value as an object key whose interface is the same type 如何为具有相似名称的变量列表创建 typescript 接口? - How can I create a typescript interface for a list of variables with similar names? 在 TypeScript 中,如何获取值属于给定类型的 object 类型的键? - In TypeScript, how to get the keys of an object type whose values are of a given type? 接口的属性仅在TypeScript中的类型为A和B? - Interface whose properties are of types A and B only in TypeScript? 可以转两种吗<k, v>进入具有这些类型的单个字段的 object 类型?</k,> - Can I turn two types <K, V> into an object type with a single field with those types? 通用 React 组件接口,其属性类型可以根据特定属性更改 - Generic React Component Interface whose property types can change based on specific property 具有替代类型的接口字段 - Interface field with alternative types
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM