「TS类型体操00268」实现 If

125 阅读2分钟

题目

实现一个 If 类型,它接收一个条件类型 C ,一个判断为真时的返回类型 T ,以及一个判断为假时的返回类型 F。 C 只能是 true 或者 false, T 和 F 可以是任意类型。

例如:

// 返回类型 'a'
type A = If<true, 'a', 'b'>  
// 返回类型 'b'
type B = If<false, 'a', 'b'> 

原题链接

实现 If

思路

类比 JavaScript

类比 JavaScript,就是实现一个函数:

这个函数接收 3参数,第一个是布尔值,第二个是真值,第三个是假值。当布尔值为真时返回真值,当布尔值为假时返回假值

function myIf(myBool, T, F) {
    if (Object.prototype.toString.call(myBool) !== '[object Boolean]') {
        throw new TypeError('第 1 个参数必须是布尔类型!');
    }
    return myBool ? T : F;
}

提取逻辑点

1. 约束第 1 个参数必须是布尔类型,如果不是就抛出错误。
2. 如果第 1 个参数是 true 就返回 T, 是 false 就返回 F。

再把逻辑点翻译成 TypeScript 即可。

翻译成 TypeScript

约束第 1 个参数必须是布尔类型,如果不是就抛出错误

通过 extends 来约束类型:

type If<C extends boolean,T,U> = ...

如果第 1 个参数是 true 就返回 T,是 false 就返回 F

通过 extends 检查是否为 true, 通过三元运算符实现逻辑分支:

type If<C extends boolean,T,U> = C extends true ? T : U;

类型兼容性引发的错误

但是在TS的非严格模式下,有一个测试用例没有通过:

// @ts-expect-error
type error = If<null, 'a', 'b'>
// 期待报错而没有报错,本测试用例不通过❌

这个测试用例是将一个 null 传了进去,而 IfT 是明确声明 extendsboolean 的,没有报错说明 null 可以赋值给 boolean 类型,这是不对的。

这时就要提到类型兼容性的问题,在 TS 的非严格模式下,null 是可以赋值给 boolean 类型的,查看TS类型兼容性文档获取详细信息。

所以,这里把 TS 的严格模式打开即可,打开 tsconfig.json 文件,在 compilerOptions 选项中设置 strictNullCheckstrue

当然,要保证总开关 compilerOptions.stricttrue

实现

综上所述,最终的类型工具 If 实现为:

type If<C extends boolean,T,U> = C extends true ? T : U;