[英]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.