declare function pick(target: any, ...keys: any): any他的用户默默的写下了这段代码:
pick(undefined, 'a', 1).b写完运行,发现问题大条了,控制台一堆报错,接口数据也提交不上去了,怎么办呢?
declare function pick(target: Record<string, unknown>, ...keys: string[]): unknown很好,上面的问题便不复存在了,API 也是基本可用的了。但是!当对象复杂的时候,以及字段并不是短单词长度的时候就会发现了一个没解决的问题。
pick({ abcdefghijkl: '123' }, 'abcdefghikjl')从肉眼角度上,我们很难发现这前后的不一致,所以我们为什么要让调用方的用户自己去 check 自己的字段有没有写对呢?
declare function pick< T extends Record<string, unknown> >(target: T, ...keys: (keyof T)[]): unknown我们又进一步解决的上面的问题,但是!还是有着相似的问题,虽然我们不用检查 keys 是不是传入的是一个正确的值了,但是我们实际上对返回的值也存在一个类似的问题。
pick({ abcdefghijkl: '123' }, 'abcdefghijkl').abcdefghikjl
declare function pick< T extends Record<string, unknown>, Keys extends keyof T >(target: T, ...keys: Keys[]): { [K in Keys]: T[K] }到这里已经是对类型的作用有了基础的了解了,能写出来符合开发者所能接受的类型相对友好的代码了。我们可以再来思考一些更特殊的情况:
// 输入了重复的 key pick({ a: '' }, 'a', 'a')
export type L2T<L, LAlias = L, LAlias2 = L> = [L] extends [never] ? [] : L extends infer LItem ? [LItem?, ...L2T<Exclude<LAlias2, LItem>, LAlias>] : never declare function pick< T extends Record<string, unknown>, Keys extends L2T<keyof T> >(target: T, ...keys: Keys): Pick<T, Keys[number] & keyof T> const x0 = pick({ a: '1', b: '2' }, 'a') console.log(x0.a) // @ts-expect-error console.log(x0.b) const x1 = pick({ a: '1', b: '2' }, 'a', 'a') // ^^^^^^^^ // TS2345: Argument of type '["a", "a"]' is not assignable to parameter of type '["a"?, "b"?] | ["b"?, "a"?]'. // Type '["a", "a"]' is not assignable to type '["a"?, "b"?]'. // Type at position 1 in source is not compatible with type at position 1 in target. // Type '"a"' is not assignable to type '"b"'.一个相对来说比较完美的 pick 函数便完成了。
3.公司整体技术栈采用的是 TypeScript ,要用 TypeScript 进行业务编写,从而为了过类型检查和 review 而去解决类型问题
4.扩展性十足