typescript
export type IfNever<T, Y = true, N = false> = [T] extends [never] ? Y : Nexport type IfUnknown<T, Y, N> = [unknown] extends [T] ? Y : N
仔细看这两行代码,发现等号右边 [T] extends [never]
和[unknown] extends [T]
的 顺序为啥不一样?
[unknown] extends [T] 为什么不能写成 [T] extends [unkonw] ?
类型推导的方向性:
- 在 TypeScript 的类型系统中,类型推导是从 "较宽泛的类型" 推导到 "较具体的类型"。
unknown
表示所有可能的类型,是所有类型中最宽泛的类型。- 当我们要检查一个类型
T
是否为unknown
时,应该从unknown
出发,推导它是否可以扩展到T
,而不是反过来。
[unknown] extends [T]
的含义:
- 这表示 "一个元素类型为
unknown
的数组是否可以扩展(assignable)到一个元素类型为T
的数组"。 - 如果
T
是unknown
,那么unknown
可以扩展到unknown
本身,条件为真,返回Y
。 - 如果
T
是其他具体类型,比如string
、number
等,那么unknown
无法扩展到这些类型,条件为假,返回N
。
为什么不写成 [T] extends [unknown]
:
- 虽然从语法上可以这么写,但这会导致类型推导逻辑的错误。
- 因为
unknown
可以扩展到所有类型,所以[unknown]
可以扩展到[T]
,无论T
是什么类型。 - 这会导致条件永远为真,始终返回
Y
,无法判断T
是否为unknown
。
实际应用:
IfUnknown
类型通常用于在条件类型中处理 unknown
类型,例如:
TypeScript
r
type SafeProp<T> = IfUnknown<T, never, T>; // 如果 T 是 unknown,返回 never,否则返回 T
function handleProp<T>(prop: SafeProp<T>) {
// 在这里可以安全地处理 prop,因为它不可能是 unknown
}
这样可以确保在函数内部,prop
的类型一定不是 unknown
,可以进行安全的操作,避免潜在的类型错误。
总结:
export type IfUnknown<T, Y, N> = [unknown] extends [T] ? Y : N
的写法更符合类型推导的逻辑,可以正确判断一个类型是否为 unknown
,并在条件类型中提供更精确的类型控制。
[T] extends [never] 为什么不能写成 [never] extends [T] ?
我会解释 export type IfNever<T, Y = true, N = false> = [T] extends [never] ? Y : N
的作用以及它在类型系统中的含义:
1. 类型推导与 never
类型:
- 在 TypeScript 中,
never
类型表示永远不会存在的值。它是所有类型的子类型,但没有任何值属于never
类型。 - 当使用类型推导来检查一个类型是否为
never
时,需要从never
类型出发,推导它是否可以扩展到该类型。
2. 条件类型的逻辑:
IfNever<T, Y = true, N = false> = [T] extends [never] ? Y : N
定义了一个条件类型。- 它的意思是:如果
T
可以扩展到never
类型,那么返回Y
,否则返回N
。
3. 为什么 [T] extends [never]
而不是 [never] extends [T]
:
- 因为
never
是所有类型的子类型,所以[never]
可以扩展到任何类型的数组[T]
,无论T
是什么类型。 - 因此,如果写成
[never] extends [T]
,条件永远为真,无法判断T
是否为never
。
4. 实际用途:
-
在类型系统中,
IfNever<T>
常用于以下场景:-
检查一个类型是否为
never
,例如:rtype IsNever<T> = IfNever<T>;
-
在条件类型中排除
never
类型,例如:TypeScript
rtype NonNever<T> = IfNever<T, never, T>; // 如果 T 是 never,返回 never,否则返回 T
-
在泛型函数中处理
never
类型,例如:TypeScript
rfunction handleValue<T>(value: T): IfNever<T, void, T> { // 如果 T 是 never,则不返回任何值 // 否则返回 value }
-
总结:
IfNever
类型是 TypeScript 中的一个有用的条件类型,用于判断一个类型是否为 never
,并在类型系统中提供更灵活的类型控制。
以上问题衍生出来两个疑问:
所有类型都是 unkonwn 的子类型吗?
在 TypeScript 中,unknown
表示所有可能的类型,是所有类型中最宽泛的类型。因此,所有类型都是 unknown
的子类型。
从类型推导的角度来看,unknown
可以扩展到所有类型 。因此,T
可以扩展到 unknown
,无论 T
是什么类型。
从类型安全的角度来看,unknown
可以表示任何类型的值,包括未知的值。因此,将 unknown
赋值给其他类型的变量是安全的。
以下是一些例子:
typescript
// 所有类型都是 `unknown` 的子类型
let a: number = 1;
let b: unknown = a; // 合法
// `unknown` 可以扩展到所有类型
let c: unknown = 'hello';
let d: string = c; // 合法
// 将 `unknown` 赋值给其他类型的变量是安全的
let e: never = undefined;
let f: unknown = e; // 合法
never 是所有类型的 子类型吗?
在 TypeScript 中,never
表示永远不会存在的值。因此,never 不是任何类型的子类型。(你可以这样认为,但这是错误的说法)
从类型推导的角度来看,never
不能扩展到任何类型 。因此,never
不能扩展到 T
,无论 T
是什么类型。
从类型安全的角度来看,never
表示永远不会存在的值。因此,将 never
赋值给其他类型的变量是不可能的。
以下是一些例子:
typescript
// `never` 不是任何类型的子类型
let a: number = 1;
let b: never = a; // 不合法
// `never` 不能扩展到任何类型
let c: unknown = 'hello';
let d: never = c; // 不合法
// 将 `never` 赋值给其他类型的变量是不可能的
let e: string = undefined;
let f: never = e; // 不合法
需要注意的是,never
类型的值永远不会存在,因此无法使用。