awesome-typescript
awesome-typescript copied to clipboard
「重学TS 2.0 」TS 练习题第二题
第二题
本道题我们希望参数 a 和 b 的类型都是一致的,即 a 和 b 同时为 number 或 string 类型。当它们的类型不一致的值,TS 类型检查器能自动提示对应的错误信息。
function f(a: string | number, b: string | number) {
if (typeof a === 'string') {
return a + ':' + b; // no error but b can be number!
} else {
return a + b; // error as b can be number | string
}
}
f(2, 3); // Ok
f(1, 'a'); // Error
f('a', 2); // Error
f('a', 'b') // Ok
请在下面评论你的答案
// 2 函数重载
type CustomType = string | number;
function f(a: number, b: number): number;
function f(a: string, b: string): string;
function f(a: number | string, b: number | string) {
// 答案给的代码
// if (typeof a === 'string') {
// return a + ':' + b;
// } else {
// return (a as number) + (b as number);
// }
// 这样更直观一些
if (typeof a === 'string' || typeof b === 'string') {
return a + ':' + b;
} else {
return a + b
}
}
f('s', 'k');
f('s', 1);
f(1, 'k');
f(1, 2)
2.1 函数重载
function f(a: string, b: string): string
function f(a: number, b: number): number
function f(a: string | number, b: string | number ): string | number {
if (typeof a === 'string') {
return a + ':' + b;
} else {
return ((a as number) + (b as number));
}
}
f(2, 3); // Ok
f(1, 'a'); // Error
f('a', 2); // Error
f('a', 'b') // Ok
延伸阅读:TypeScript 函数重载
2.2 把参数组合成一种类型
const isStrArr = (a: string[] | number[]): a is string[] => typeof a[0] === 'string'
function f(...args: string[] | number[]) {
if (isStrArr(args)) {
return args[0] + ':' + args[1];
} else {
return args[0] + args[1];
}
}
f(2, 3); // Ok
f(1, 'a'); // Error
f('a', 2); // Error
f('a', 'b') // Ok
这样ok吗?
function f<T extends string | number>(a: T, b: T) {
if (typeof a === 'string') {
return a + ':' + b; // no error but b can be number!
} else {
return (a as number) + (b as number); // error as b can be number | string
}
}
f(2, 3); // Ok
f(1, 'a'); // Error
f('a', 2); // Error
f(2, 2) // Ok
function add(a: number, b: number): number; function add(a: string, b: string): string; function add(a: string | number, b: string | number): string | number { if (typeof a === 'number' && typeof b === 'number') { return `${a}+${b}=${a + b}`; } else { return `${a}+${b}=${a}${b}`; } }
function f(a: string, b: string): string
function f(a: number, b: number): number
function f(a: string | number, b: string | number) {
if (typeof a === 'string') {
return a + ':' + b // no error but b can be number!
} else {
return a + +b // error as b can be number | string
}
}
f(2, 3) // Ok
f(1, 'a') // Error
f('a', 2) // Error
f('a', 'b') // Ok
思路:首先用函数重载约束参数相同类型,然后再根据不同类型执行不同操作
function f(a: string, b: string): string
function f(a: number, b: number): number
function f(a: string | number, b: string | number) {
if (typeof a === 'string') {
return a + ':' + b // no error but b can be number!
} else {
return a + +b // error as b can be number | string
}
}
f(2, 3) // Ok
f(1, 'a') // Error
f('a', 2) // Error
f('a', 'b') // Ok
function f(...args: string[] | number[]) {
if (typeof args[0] === 'string') {
return args[0] + ':' + args[1]; // no error but b can be number!
} else {
return (args[0] as number) + (args[1] as number); // error as b can be number | string
}
}
f(2, 3); // Ok
f(1, 'a'); // Error
f('a', 2); // Error
f('a', 'b') // Ok
函数重载
function f(a: string, b: string): string
function f(a: number, b: number): number
function f(a: string | number, b: string |number) {
if (typeof a === 'string') {
return a + ':' + b; // no error but b can be number!
} else {
return a + +b; // error as b can be number | string
}
}
用元祖固定类型
type F = <T extends string | number>(...args: [T, T]) => string | number
const f: F = (a, b) => {
if (typeof a === 'string') {
return a + ':' + b; // no error but b can be number!
} else {
return +a + +b; // error as b can be number | string
}
}
function f(a: string, b: string): string;
function f(a: number, b: number): number;
function f(a: string | number, b: string | number): string | number {
if (typeof a === 'string' && typeof b === 'string') {
return a + ':' + b; // no error but b can be number!
} else if (typeof a === 'number' && typeof b === 'number') {
return a + b; // error as b can be number | string
}
throw Error('has error, a and b must be same type');
}
f(2, 3); // Ok
f(1, 'a'); // Error
f('a', 2); // Error
f('a', 'b') // Ok
通过函数重载来实现不同类型参数的定义。同时调整原有的代码,增加一个兜底的throw error,这样在运行时也能有类型保护
function f(a: string | number, b: string | number) { if (typeof a === 'string') { return a + ':' + b; } else { return b === 'number' ? a + b : a + '' + b; } }
console.log(f(2, 3)); console.log(f(1, 'a')); console.log(f('a', 2)); console.log(f('a', 'b'))
// 使用函数重载可以实现 type types = string | number function f(a: string, b: string): string function f(a: number, b: number): number function f(a: types, b: types) { if (typeof a === 'string' || typeof b === 'string') { return a + ':' + b; // no error but b can be number! } else { return a + b; // error as b can be number | string } }
f(2, 3); // Ok f(1, 'a'); // Error f('a', 2); // Error f('a', 'b') // Ok
function f(a:number,b:number):number function f(a:string,b:string):string
function f(a: string | number, b: string | number) { if (typeof a === 'string') { return a + ':' + b; // no error but b can be number! } else if(typeof b === 'number'){ return a + b; // error as b can be number | string } }
f(2, 3); // Ok f(1, 'a'); // Error f('a', 2); // Error f('a', 'b') // Ok
function f(a: number, b: number): number;
function f(a: string, b: string): string;
function f(a: number | string, b: number | string) {
if (typeof a === "string") {
return a + ":" + b;
} else {
return (a as number) + (b as number);
}
}
f("s", "k");
f("s", 1);
f(1, "k");
f(1, 2);
type a = (x: string, y: string)=> string | number
type b = (x: number, y: number)=> string | number
const fn: a & b = (x, y) => {
if(typeof x === 'string' || typeof y === 'string') {
return x + ':' + y
}
return x + y
}
function f(a: string, b:string): string; function f(a: number, b:number): number; function f(a: string | number, b: string | number): number | string { if (typeof a === 'string') { return a + ':' + b; // no error but b can be number! } else { return a + (b as number); // error as b can be number | string } }
type T = number | string
function f(a: string, b: string): string
function f(a: number, b: number): number
function f(a: T, b: T) {
if (typeof a === 'string') {
return a + ':' + b;
} else {
return a + +b
}
}
f(2, 3); // Ok
f(1, 'a'); // Error
f('a', 2); // Error
f('a', 'b') // Ok
函数重载、类重载是 JS 的叛徒,能避免就避免,怎么能对同一个东西多次定义,然后在调用时去做选择。 这是偷懒,把麻烦后置。
最后为了逻辑更健壮,可以利用never特性,来避免没有穷尽的情况。
function f<T extends string | number>(a: T, b: T) {
if (typeof a === 'string') {
return a + ':' + b
} else if (typeof a === 'number') {
return (a as number) + (b as number)
} else {
const check: never = a
return ''
}
}


function f<T extends string | number>(a: T, b: T) { if (typeof a === 'string') { return a + ':' + b; // no error but b can be number! } else { return (a as number) + (b as number); // error as b can be number | string } }
没有必要用T;因为T extend string | number没有意义;因为T就是 string | number;没有必要extend。这个方法不好。
type stringOrnumber=string|number
function f<T extends stringOrnumber>(a:T,b:T):stringOrnumber { if (typeof a === 'string') { return a + ':' + b; // no error but b can be number! } else { return (a as number) + (b as number); // error as b can be number | string } }
f(2, 3); // Ok console.log(f(1, 'a')); // Error console.log(f('a', 2)); // Error f('a', 'b') // Ok
export default {}
// 类型约束
type Type = string | number
function f<T extends Type>(a: T, b: T) {
if (typeof a === 'string') {
return a + ':' + b; // no error but b can be number!
} else {
return (a as number) + (b as number); // error as b can be number | string
}
}
f(2, 3); // Ok
f(1, 'a'); // Error
f('a', 2); // Error
f('a', 'b') // Ok
// 函数重载
type sign = number | string
function f(a:number, b:number):number
function f(a:string, b:string):string
function f(a:sign, b:sign): sign {
if(typeof a === 'string'){
return a + '' +b // no error but b can be number!
}
else {
return (a + (b as number)) // error as b can be number | string
}
}
f(2, 3); // Ok
f(1, 'a'); // Error
f('a', 2); // Error
f('a', 'b') // Ok
function f(a: string | number, b: string | number) {
if (typeof a === 'string' && typeof b === 'string') {
return a + ':' + b;
} else if (typeof a === 'number' && typeof b === 'number') {
return a + b;
}
}
运行时需要校验的地方, 就应该交给运行时来判断
function f(a: string | number, b: string | number) { if (typeof a === 'string' && typeof b === 'string') { return a + ':' + b; } else if (typeof a === 'number' && typeof b === 'number') { return a + b; } }运行时需要校验的地方, 就应该交给运行时来判断
这个好像不行喔,有报错Not all code paths return a value.
函数重载、类重载是 JS 的叛徒,能避免就避免,怎么能对同一个东西多次定义,然后在调用时去做选择。 这是偷懒,把麻烦后置。
最后为了逻辑更健壮,可以利用
never特性,来避免没有穷尽的情况。function f<T extends string | number>(a: T, b: T) { if (typeof a === 'string') { return a + ':' + b } else if (typeof a === 'number') { return (a as number) + (b as number) } else { const check: never = a return '' } }
确实这才是比较好的做法
function f(a: string | number, b: string | number):string|number { if (typeof a === 'string'&&typeof b === 'string') { return a + ':' + b; // no error but b can be number! } else if(typeof a === 'number'&&typeof b === 'number') { return a + b; // error as b can be number | string }else{ throw Error } }
f(2, 3); // Ok f(1, 'a'); // Error f('a', 2); // Error f('a', 'b') // Ok
function f(a: string, b: string) : string
function f(a: number, b: number) : number
function f(a: any, b: any) {
if (typeof a === 'string') {
return a + ':' + b; // no error but b can be number!
} else {
return a + b; // error as b can be number | string
}
}
f(2, 3); // Ok
f(1, 'a'); // Error
f('a', 2); // Error
f('a', 'b') // Ok
// 函数重载
function f(a: string, b: string): void;
function f(a: number, b: number): void;
function f(a: string | number, b: string | number) {
if (typeof a === 'string') {
return `${a}:${b}`; // no error but b can be number!
}
return a + b; // error as b can be number | string
}
f(2, 3); // Ok
f(1, 'a'); // Error
f('a', 2); // Error
f('a', 'b'); // Ok
function f<T extends string | number>(a: T, b: T) {
if (typeof a === 'string') {
return a + ':' + b; // no error but b can be number!
} else {
return (a as number) + (b as number); // error as b can be number | string
}
}
f(2, 3); // Ok
f(1, 'a'); // Error
f('a', 2); // Error
f('a', 'b') // Ok
不改动源代码好像不行
function f(a: string, b: string): string
function f(a: number, b: number): number
function f(a: string | number, b: string | number) {
if (typeof a === 'string' || typeof b === 'string') {
return a + ':' + b; // no error but b can be number!
} else {
return a + b; // error as b can be number | string
}
}
f(2, 3); // Ok
f(1, 'a'); // Error
f('a', 2); // Error
f('a', 'b') // Ok