目录
1、介绍
2、函数类型表达式
3、呼叫签名
4、构造签名
5、泛型函数
6、推论
7、约束
8、使用约束值
9、指定类型参数
1、介绍
函数是JavaScript应用程序的基础。 它帮助你实现抽象层,模拟类,信息隐藏和模块。 在TypeScript里,虽然已经支持类,命名空间和模块,但函数仍然是主要的定义 行为的地方。函数是任何应用程序的基本构建块,无论它们是本地函数、从其他模块导入的函数,还是类上的方法。 它们也是值,就像其他值一样,TypeScript 有很多方法来描述如何调用函数。 让我们学习如何编写描述函数的类型。
2、函数类型表达式
描述函数的最简单方法是使用函数类型表达式。 这些类型在语法上类似于箭头函数:
function Animal(fn: (age: number)=>void){
return fn(20);
}
function Cat(age: number) {
console.log('age: ', age);
}
// age: 20
Animal(Cat)
语法(age: number) => void
表示“具有一个参数的函数,参数为数字类型,没有返回值”。 就像函数声明一样,如果未指定参数类型,则是隐式的 any 类型。
fn: (age: number)=>void 形成一个函数表达式,参数是必须的,需声明对应的类型,否则具有隐藏式的类型 any。
我们可以把函数类型表达式赋值给类型别名,如下所示:
type expFunc = (age: number) => void;
function Animal(fn: expFunc) {
return fn(20);
}
function Cat(age: number) {
console.log('age: ', age);
}
// age: 20
Animal(Cat)
3、呼叫签名
在 JavaScript 中,函数除了可调用之外,还可以具有属性。 但是,函数类型表达式语法不允许声明属性。 如果我们想描述一些可以用属性调用的东西,我们可以在对象类型中写一个调用签名:
type DescInfo = {
desc: string;
(args: number): number
};
function Animal(fn: DescInfo) {
// 我是tom🐱今年2岁
console.log(fn.desc + '今年' +fn(2) + '岁');
}
function Cat(age: number) {
return age;
}
Cat.desc = "我是tom🐱";
Animal(Cat)
请注意,与函数类型表达式相比,语法略有不同 - 在参数列表和返回类型之间使用,而不是上面缩写的(fn: (age: number)=>void)这样。
4、构造签名
JavaScript 函数也可以与运算符一起调用。 TypeScript 将这些称为构造函数,因为它们通常会创建一个新对象。 您可以通过在呼叫签名前面添加关键字来编写构造签名:new
type DescInfo = {
new (args: number): Cat
};
function Animal(fn: DescInfo) {
return new fn(20);
}
class Cat {
age: number;
constructor(age: number){
this.age = age;
}
}
console.log(new Cat(20).age); // 20
某些对象,如 JavaScript 的对象,可以带或不带 . 您可以任意组合同一类型的调用和构造签名:
interface Animal {
new(name: string, birthday: Date);
(n?:number): number;
}
5、泛型函数
通常编写一个函数,其中输入的类型与输出的类型相关,或者两个输入的类型以某种方式相关。
function Animals(array: any[]) {
return array[1];
}
如果函数返回数组元素的类型会更好,例如:any 或者其他类型
在 TypeScript 中,当我们想要描述两个值之间的对应关系时,会使用泛型。 我们通过在函数签名中声明一个类型参数来做到这一点:
function Animals<Type>(array: any[]) : Type | undefined {
return array[1];
}
在函数定义是,指定泛型Type,在返回的时候也指定对应类型 Type | undefined,这样返回的结果就会事先定义的范围,这样也使得结果更可控。
function Animals<Type>(array: any[]) : Type | undefined {
return array[1];
}
console.log(Animals([1,2])); // 2
6、推论
我们不必在此示例中指定。 类型是由 TypeScript 推断的 - 自动选择的。
我们也可以使用多个类型参数。 例如
function Animals<Input, Output>(arr: Input[], func: (arg: Input) => Output) : Output[] {
return arr.map(func)
}
Animals([1,2,3], (n)=>console.log(n)); // 1 2 3
7、约束
我们编写了一些可以处理任何类型的值的通用函数。 有时我们想关联两个值,但只能对某个值子集进行操作。 在这种情况下,我们可以使用约束来限制类型参数可以接受的类型类型。
function longest<Type extends { length: number }>(a: Type, b: Type) {
if (a.length >= b.length) {
return a;
} else {
return b;
}
}
const longerArray = longest([1, 2], [1, 2, 3]);
const longerString = longest("alice", "bob");
const notOK = longest(123, 100);
// longest 指定类型必须有长度 ,字符串和数组都有长度,而数组没有length
在此示例中,有一些有趣的事项需要注意。 我们允许 TypeScript 推断返回类型 。 返回类型推理也适用于泛型函数(longest
)。
因为我们被限制为 ,所以我们被允许访问 和 参数的属性。 如果没有类型约束,我们将无法访问这些属性,因为这些值可能是没有 length 属性的其他类型的值。Type
{ length: number }
.length a b
和的类型是根据参数推断的。 请记住,泛型都是关于将两个或多个值与同一类型相关联!longerArraylongerString
8、使用约束值
function minimumLength<Type extends { length: number }>(
obj: Type,
minimum: number
): Type {
if (obj.length >= minimum) {
return obj;
} else {
return { length: minimum }; // 报错...
}
}
发现else里面的return,报错了,如下所示:
不能将类型“{ length: number; }”分配给类型“Type”。
"{ length: number; }" 可赋给 "Type" 类型的约束,但可以使用约束 "{ length: number; }" 的其他子类型实例化 "Type"。
如果返回是Type类型,可以调用函数试一下
const arr = minimumLength([1, 2, 3], 6);
console.log(arr.slice(0)); // [1, 2, 3]
如果把上面返回值改成obj,之后,上面代码是可以执行。
9、指定类型参数
TypeScript 通常可以在泛型调用中推断预期的类型参数,但并非总是如此。 例如,假设您编写了一个函数来组合两个数组:
function arrayConcat<Type>(arr1: Type[], arr2: Type[]): Type[] {
return arr1.concat(arr2);
}
下面测试一下,传不同类型是否会报错提示:
console.log(arrayConcat([1,2,3], ['name'])); // 不能将类型“string”分配给类型“number”。
console.log(arrayConcat([1,2,3], [true])); // 不能将类型“boolean”分配给类型“number”。
console.log(arrayConcat([1,2,3], [4, 5, 6]));
console.log(arrayConcat([1,2,3], [undefined]));
console.log(arrayConcat([1,2,3], [null]));
根据以上测试结果,可以看出前2种会有对应错误提示。
如果能提前知道类型的参数,可以提前指定类型参数。如下所示:
console.log(arrayConcat<number | string>([1,2,3], ['name']));
console.log(arrayConcat<number | boolean>([1,2,3], [true]));