TypeScript条件类型扩展对象,仅适用于属性

时间:2019-10-01 10:24:41

标签: typescript

我想实现类型的条件行为。当我将泛型以对象类型(数组,对象,元组,记录),实际上是任何复合类型的形式放置时,我希望该类型表现为该对象的typeof字段,但是当给定类型是主类型时,我想是同一类型。

type B<A> = A extends object ? A[keyof A] : A;
const tuple: [1, 2];
const element:B<typeof tuple> = 2; // # ISSUE - element can be also any method of array

const num: 1;
const numElment:B<typeof num> = 1;

以上代码可以使用,但是对于复合B类型,我还可以分配Array类型中的所有方法类型。我的问题是-如何指定我只对不是功能的东西感兴趣。因此,只有纯字段(例如以元组为例,element才应该是数字。

我也尝试使用extends {}extends {[a: string | number]: any},但是它也没有用,甚至在以上代码段之后也会中断。

1 个答案:

答案 0 :(得分:1)

如果您确实想从类型联合中排除函数,则可以使用Exclude<T, U> utility type

type ExcludeFunctionProps<T extends object> = Exclude<T[keyof T], Function>;

但是我不认为这确实可以满足您的要求:

type Hmm = ExcludeFunctionProps<["a", "b"]>; // "a" | "b" | 2

这里Hmm"a" | "b" | 2。为什么2?因为数组具有数字类型的length属性,而对元组具有该类型作为数字文字类型2。除非您打算包括元组的长度,否则可能不是这样。

type EvenWeirder = ExcludeFunctionProps<[string, () => void]>; // string | 2

在元组或对象显式包含类似函数的值的情况下,这也会将它们切掉。在我看来,这绝对是一种奇怪的行为。


相反,我认为您遇到了the issue,其中keyof T仍然是所有数组方法和属性的完整列表,即使您可以map over array types没有它们。因此,我在这里尝试做的是制作自己的KeyofAfterMapping<T>,对于非数组类型返回keyof T,但对于数组类型仅保留number索引键。像这样:

type KeyofAfterMapping<T> = Extract<
  keyof T,
  T extends any[] ? number : unknown
>;

让我们看看它的作用:

type KeyofPair = KeyofAfterMapping<["a", "b"]>; // number
type KeyofArray = KeyofAfterMapping<string[]>; // number
type KeyofObject = KeyofAfterMapping<{ a: string; b: number }>; // "a" | "b"

现在我们可以指定B

type B<A> = A extends object ? A[KeyofAfterMapping<A>] : A;

并验证其行为是否符合您的预期:

type TryTupleAgain = B<["a", "b"]>; // "a" | "b"
type KeepsFunctions = B<Array<() => void>>; // () => void
type ObjectsAreStillFine = B<{ a: string; b: number }>; // string | number
type PrimitivesAreStillFine = B<string>; // string

看起来不错。好的,希望对您有所帮助。祝你好运!

Link to code