let f = (fn:(e:any)=>void = c) => {}
let c = ()=> 0 // 类型: () => number
// 上面的函数可以作为默认参数
// 而在下面这个就会 Error
let t = (f:number = '22') =>{}
主要是第一个为什么可以作为默认参数?
第二个大家应该都知道
let f = (fn:(e:any)=>void = c) => {}
let c = ()=> 0 // 类型: () => number
// 上面的函数可以作为默认参数
// 而在下面这个就会 Error
let t = (f:number = '22') =>{}
主要是第一个为什么可以作为默认参数?
第二个大家应该都知道
(e: any)
表示任意参数,包括 undefined
,包含了没有参数的情况 —— 或者可以这样理解:
由于 fn
的参数是 e: any
,所以在内部可以传入任意参数,当把 c
作为实参传入之后,也可以给 c()
传入任意参数,而不影响接口,也不可能影响 c
的内部行为(因为 c
压根不会使用这个参数)。如果把 c
的参数声明为 (a: any, b: any)
就不行了,因为调用 c
需要传入两个参数,而在 f
的参数 fn
声明只需要一个参数,在使用调用时就可能不会传入第 2 个参数,不符合 c
的参数要求。
fn
的类型(返回类型)是 void,说明在 f
函数体中不需要使用它的返回值,那么对传入的 fn
,其实并不需要约束返回值类型。如果 fn
声明返回类型是 : string
,那么 c
就不参传入,因为 c
的返回类型是 int
不符合 fn
的约束。如果允许传入 c
作为 fn
,那么 f
中拿到 fn
(即 c
) 返回值进行后续处理的时候就可能产生类型错误(以为是 string
,但实际是 number
)。
我觉得说得不清楚,还是举例来得直接些:
function f(fn: (a: string) => string | number) {
const s = "hello"; // string 类型
const r = fn(s); // string 类型
return typeof r === "string"
? r.toUpperCase()
: r.toFixed(2);
}
// 成功的类型匹配
f(
// 参数类型是 fn 参数类型的超集,所以符合传入 fn 条件的参数
// 都符合传入下面这个函数,比如 "hello" (string 类型)
(a: string | number) => a.toString()
// 返回类型是 string (由 .toString() 推导而来)
// 是 fn 返回类型的子集,所以它的返回值
// 一定可以被后续代码(就是 typeof r ...)处理
);
// 不成功的类型匹配 - 参数
f(
// 参数类型 number 不是 fn 参数类型 string 的超集
// 所以传递给 fn 的参数,不能给下面这个函数使用(比如 "hello")
(a: number) => a.toString()
);
// 不成功的类型匹配 - 返回类型
f(
(a: string) => { }
// 该函数无返回,推导返回类型是 void,
// 其返回值不可能给调用 fn 的后续代码使用
// 也就是 `typeof r ...` 可能会出错,违反类型安全约束
);
要注意看提示信息啊,把t改成这样就行