前端React Nextjs中的TS类型过滤实用技巧

作者:青柠时间:2026-10-25 09:35:42浏览:139

本文深入解析TypeScript中利用Pick、keyof及条件类型实现类型过滤的高级技巧。通过拆解FilterConditionally类型,讲解如何遍历对象属性并筛选指定类型,结合React源码实例,帮助开发者掌握静态类型校验的核心方法,提升代码质量与可维护性。

大家好,我是零一。在查阅同事代码或优秀开源库时,大家或许见过各种复杂的TS写法,若不花时间钻研很难理解。若由我们自己编写,可能直接选用any即可,但随着项目规模扩大,这些TS高级技巧的重要性便凸显出来,因为它们能高效协助完成静态类型校验。

自我介绍

TS类型过滤(英文名为FilterConditionally,此为笔者自创名称)的完整定义如下所示。

type FilterConditionally<Source, Condition> = Pick<
  Source, 
  {
    [K in keyof Source]: Source[K] extends Condition ? K : never
  }[keyof Source]
>;

尽管该类型定义看似复杂,但实际应用价值极高。它能够从一个对象类型中精准筛选出符合特定条件的属性,例如:

interface Example {
    a: string;
    b: string; 
    c: number; 
    d: boolean; 
}
type NewType = FilterConditionally<Sample, string>
/*
	NewType 最终结果为:
	{
		a: string;
		b: string
	}
*/

相信读者已经了解了该类型的作用,并且渴望深入理解其原理。接下来,我们将由内而外、分步骤进行解析,确保大家能够完全掌握。若仍有疑问,欢迎在评论区交流(注:此为玩笑话)。

分步介绍

由于涉及的知识点较多,为避免不熟悉TS的读者感到困惑,我们先介绍其中几个常见的基础知识点。

开胃小菜

这部分内容不会占用太多时间,熟悉这些概念的读者可以直接跳过。

keyof

关键词keyof被称为索引类型查询操作符,其作用正如字面意思所示:获取对象的key值。

interface Example {
  a: string;
  b: string;
  c: number;
  d: boolean;
}
type Keys = keyof Example   // 等价于 type Keys = 'a' | 'b' | 'c' | 'd'

可以将keyof简单理解为JavaScript中的Object.keys方法。

in

关键词in用于遍历枚举类型,具体示例如下:

type Keys = 'a' | 'b' | 'c' | 'd'
type Obj = {
  [T in Keys]: string;  // 遍历Keys,把每个key都赋值string类型
}
/* 等价于 
	 type Obj = {
	   a: string;
	 	 b: string;
		 c: string;
		 d: string;
	 }
*/

可以将in简单理解为JavaScript中for...in循环里的in关键字的作用。

Conditional

第二个知识点是条件判断,例如:

interface A {}
interface B extends A {}  // B继承于A
// B是否继承于A?若是,则为number类型;若不是,则为string类型
type C = B extends A ? number : string  // 等价于 type C = number
// A是否继承于B?若是,则为number类型;若不是,则为string类型
type D = A extends B ? number : string  // 等价于 type D = string

可以将A extends B ? number : string简单理解为JavaScript中的三元运算符。

泛型

关于泛型的详细介绍此处略过,不了解的读者可直接查阅TS文档中的泛型部分。

正餐开始

介绍完基础知识点后,趁热打铁来看一个简单的类型定义。

type MarkUnwantedTypesAsNever<Source, Condition> ={
  [K in keyof Source]: Source[K] extends Condition ? K : never
}

一句话概括该类型的作用:遍历一个对象类型,将不符合条件的类型标记为never。

举个例子:

interface Example {
    a: string; 
    b: string; 
    c: number; 
    d: boolean; 
}
// 我只想要Example类型中的string类型的key,非string的就标记为never
type MyType = MarkUnwantedTypesAsNever<Example, string>
/*
	等价于:
	type MyType = {
		a: 'a';
		b: 'b';
		c: never;
		d: never;
	}
*/

简要说明其中的细节:[K in keyof Example]遍历了Example对象类型,随后使用条件判断Source[K] extends Condition ? K : never为对应的key值赋值。假设遍历的第一个key值为a,那么Source[K] = Source[a] = string,此时即为string extends string ? 'a' : never。由于string确实继承于string,因此得到了上述结果。

此时读者可能会疑惑,为什么要将类型处理成这样?我们最终想要的结果不是获取一个包含特定key-value的对象类型吗?别急,后续还有其它操作。

再来看一个关于索引访问接口属性的小知识点。

type Value = {name: "zero2one"}["name"]  // 等价于 type Value = "zero2one"

可以将其简单理解为JavaScript中访问对象某个key对应的value。

而在TS中还存在另一种情况:

type Value = {
  name: "zero2one"; 
  age: 23
}["name" | "age"]
// 等价于 type Value = "zero2one" | 23

值为never的key值是无法被访问到的:

type Value = {
  name: "zero2one"; 
  age: never
}["name" | "age"]
// 等价于 type Value = "zero2one"

因此,接下来可以查看更复杂的类型定义。

type MarkUnwantedTypesAsNever<Source, Condition> ={
  [K in keyof Source]: Source[K] extends Condition ? K : never
}[keyof Source]

我们巧妙地利用keyof关键词去遍历访问所有的接口属性。

// 借用一下刚才例子的结果
type MyType = {
  	a: 'a';
		b: 'b';
		c: never;
		d: never;
}['a' | 'b' | 'c' | 'd']
/*
	等价于:
	type MyType = 'a' | 'b'
*/

至此,我们所做的事情就是:把目标对象类型中符合指定类型的key值筛选了出来。

别急别急,离成功只差一步之遥。

最后登场的是Pick类型,这是TS内置的类型,简单了解一下它的作用。

// Pick类型的实现
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
}

你可以不去详细读懂它的实现,只需要知道Pick的作用就是:筛选出类型T中指定的某些属性。

举个简单的例子:

interface A {
  a: 1;
  b: 2;
  c: 3;
  d: 4;
}
type C = Pick<A, 'a' | 'c'>  // 等价于 type C = { a: 1; c: 3 }

是的,就是这么简单。好了,可以来看最终的“Boss”了。

自我介绍

那么最后再从Source中筛选出对应属性即可。回到本文具体的例子当中,图中红框中的值上文已得到为type MyType = 'a' | 'b',那最后Pick一下就好了。

interface Example {
	a: string;
  b: string;
  c: number;
  d: boolean;
}
// 上文得到的结果
type MyType = 'a' | 'b'
type Result = Pick<Example, MyType>  // 等价于 type Result = { a: string; b: string }
// ---- 以上等价于 ---- //
interface Example {
    a: string; 
    b: string;   
    c: number; 
    d: boolean; 
}
type NewType = FilterConditionally<Sample, string>
/*
	NewType 最终结果为:
	{
		a: string;
		b: string
	}
*/

这就是文章开头的结果获取的全过程。

实战应用例子

正如本文标题所述,TS类型过滤在很多优秀的开源库中是非常常见的,比如我们熟悉的React中就是:

type ElementType<P = any> = {
	[K in keyof JSX.IntrinsicElements]: P extends JSX.IntrinsicElements[K] ? K : never
}[keyof JSX.IntrinsicElements] | ComponentType<P>;

最后

开源库中像TS类型过滤这种场景太多太多了,希望今后大家遇到时能轻松读懂。如果在屏幕前阅读的你是后端开发者,说不定也能在后端的开源框架源码中看到它的身影呢~

以上就是前端React Nextjs中的TS类型过滤实用技巧的详细内容,更多关于前端React Nextjs中的TS类型过滤的资料请关注本站其它相关文章!

作者最新文章
相关文章
更多