# 引用类型
在TS中提供了一些引用类型,例如:Array(数组)、String(字符串)、Date(日期对象)、RegExp(正则表达式)等。
# 初始化数组的两种方法
# 声明数组的方法
声明数组跟声明一个普通变量是一样的,都是通过 var let 关键字实现的,只不过数组的类型说明符比较复杂而已。
let arr1:number[ ] //声明一个数值类型的数组
let arr2:Array<string> //声明一个字符串类型的数组
数组是存储大量数据的集合,声明数组之后,需要给数组存储数据。这时候有两种方法:
- 字面量赋值法:直接使用[ ]对数组进行赋值。
- 构造函数赋值法: 字面量赋值法
//定义一个空数组,数组容量为0
let arr1:number[] = []
//定义一个数组时,直接给数组赋值
let arr2:number[] = [1,2,3,4,5]
//定义数组 的同时给数组赋值
let arr3:Array<string> = ['jspang','技术胖','xxx']//使用了内置的泛型Array
let arr4:Array<boolean> = [ true,false,false]
# ts数组和元组的处理
- 数组 :在元素类型后面加上[]或者使用内置泛型
let arr: number[] = [1, 2];
let arr1: Array<number> = [1, 2];
- 多维数组
var arr_name:number[][][]=[ [[1]],[[2,3,4]] ]
console.log(arr_name)
- TypeScript 可以通过
ReadonlyArray<T>设置数组为只读,那么它的所有写方法都会失效。
let arr: ReadonlyArray<number> = [1,2,3,4,5];
arr[0] = 6; // Index signature in type 'readonly number[]' only permits reading
- “readonly”类型修饰符仅允许用于数组和元组文字类型。
let arr:readonly number[] = [1,2,3,4,5];
console.log(arr)
//let arr1:readonly Array<number> = [1,2,3,4,5];
//'readonly' type modifier is only permitted on array and tuple literal types.
//console.log(arr1)
- 元组
- 元组类型用来表示 已知元素数量和类型的数组 ,各元素的类型不必相同,对应位置的类型需要相同。
const list: [string, number] = ['Sherlock', 1887] // ok
- 元组要注意元组的越界问题,虽然可以越界添加元素(不建议),但是 不可越界访问
const list: [string, number] = ['Sherlock', 1887]
list.push('hello world')
console.log(list) // ok [ 'Sherlock', 1887, 'hello world' ]
// 测试是可以通过forEach等不需要下标操作的方法获取值
list.forEach((el,i)=>{
console.log(el)
})
// Sherlock
// 1887
// hello world
// console.log(list[2]) // Tuple type '[string, number]' of length '2' has no element at index '2'
- 元组类型允许在元素类型后缀一个 ? 来说明元素是可选的
let list: [number, string?, boolean?]
list = [10, 'Sherlock', true]
list = [10, 'Sherlock']
console.log(list)
// list = [10]
- 元组类型的 Rest 使用,元组可以作为参数传递给函数,函数的 Rest 形参可以定义为元组类型
declare function rest(...args: [number, string, boolean]): void
<===>等价
declare function rest(arg1: number, arg2: string, arg3: boolean): void
const list: [number, ...string[]] = [10, 'a', 'b', 'c']
list.push(122)
console.log(list[4])//122
/**
* TS 数组和 JS 数组是一样的
* 变量提示,const numberArr: number[]
*/
const numberArr = [1, 2, 3];
const arr: number[] = [1,2,3];
const stringArr: string[] = ['a', 'b', 'c'];
// 如果这个数组里面既存数字又存字符串,如何写
const arr1: (number | string)[] = [1, '2' ,3];
// 除了基本类型的数组,对象类型的数组怎么写
const objectArr: {name: string, age: number}[] = [{name:'a', age:16}]
// 将上面的写法简化下,利用 type alias 类型别名
type User = {name: string, age: number}
const objectArr1: User[] = [{name:'a', age:16}]
// 元组 tuple
const teacherinfo = ['zina', 'girl', 18];
/**
* 鼠标悬浮变量
* const teacherinfo: (string | number)[],
* 推断出 teacherinfo 里面存储的值可能是 string, 也可能是 number
*/
const teacherinfo1: (string | number)[] = ['zina', 'girl', 18];
/**
* 如果说后面的这个数组,长度肯定只有三个,
* 第一个肯定是字符串,第二个肯定是字符串,第三个肯定是数字
* 这个时候第一个改成数字不会报错,数组这种类型约束已经约束不到了
* 这个时候元组的作用就显现了
*/
const teacherinfo2: [string, string, number] = ['zina', 'girl', 18];
/**
* 这种就是元组的写法,这个时候后面加一个,或者改动三者的类型都会报错
* 一个数组里面的长度和类型都是固定的时候,就可以使用元组
* 场景:读取 excel csv 的时候
*/
const teacherinfo3: [string, string, number][] = [
['zina', 'girl', 18],
['zina', 'girl', 18],
['zina', 'girl', 18]
];
# 数组的弊端和元组的应用场景
// hook: useState
// const [counter, setCounter] = {counter: , setCounter:}
function useState<T>(state: T) {
let currentState = state
const changeState = (newState: T) => {
currentState = newState
}
const info: [string, number] = ["abc", 18]
const tuple: [T, (newState: T) => void] = [currentState, changeState]
return tuple
}
//可以清晰的判断数据的格式,而不是只能使用any
const [counter, setCounter] = useState(10);
setCounter(1000)
const [title, setTitle] = useState("abc")
const [flag, setFlag] = useState(true)
# ts日期对象 正则
let d1:Date = new Date('2018/09/06 05:30:00')
let d2:Date = new Date('2018-09-06 05:30:00')
let d3:Date = new Date('2018-09-06T05:30:00')
console.log(d1)
console.log(d2)
console.log(d3)
let reg1:RegExp = new RegExp("jspang") //表示字符串规则里含有jspang
console.log(reg1)
let reg2:RegExp = new RegExp("jspang",'gi')
console.log(reg2)
let reg3:RegExp = /jspang/
let reg4:RegExp = /jspang/gi
# ts Promise中类型的使用
- 在 Promise 的情况下,泛型允许指定 Promise 将解决为什么类型的值。
new Promise<string>((resolve) => {
resolve('abc')
}).then((res) => {
console.log(res.length)
})
# TS函数
函数类型:
(num1: number, num2: number) => void//代表的就是一个函数类型
参数类型注解:当声明一个函数的时候,可以在每个参数后面添加一个类型注解,声明函数可以接受什么类型的参数。当参数有了类型注解的时候,TypeScript 便会检查函数的实参:
// Parameter type annotation
function greet(name: string) {
console.log("Hello, " + name.toUpperCase() + "!!");
}
greet(100)// 类型“number”的参数不能赋给类型“string”的参数。
返回值类型注解:也可以添加返回值的类型注解,跟变量类型注解一样,也不需要总是添加返回值类型注解,TS会基于它的 return 语句推断函数的返回类型。
- 基本格式
function add(x: number, y: number): number {
return x + y;
}
上面的代码只是对 = 等号右侧的匿名函数进行了类型定义,等号左侧的 add 同样可以添加类型:
const add: (x: number, y: number) => string = function(x: number, y: number): string {
return (x + y).toString()
}
可以看到,等号左侧的类型定义由两部分组成:参数类型和返回值类型,通过 => 符号来连接。
//也可以省略左边
const add = (x: number, y: number): string => (x + y).toString()
也可以将约束条件提取出来:
let compute: (x:number,y:number) =>number
compute = (a,b) => {
console.log(a+b)
return a+b
}
compute(1,3)
函数类型表达式:最简单描述一个函数的方式是使用 函数类型表达式(function type expression)。 它的写法有点类似于箭头函数:
function greeter(fn: (a: string) => void) {
fn("Hello, World");
}
function printToConsole(s: string) {
console.log(s);
}
greeter(printToConsole);
也可以使用类型别名(type alias)定义一个函数类型
type GreetFunction = (a: string) => void;
function greeter(fn: GreetFunction) {
// ...
}
# ts函数中的this
// this是可以被推导出来 info对象(TypeScript推导出来)
const info = {
name: "why",
eating() {
console.log(this.name + " eating")
}
}
info.eating()
export {}
上面的代码是可以正常运行的,也就是TypeScript在编译时,认为this是可以正确去使用的:TypeScript认为函数 sayHello 有一个对应的this的外部对象 info,所以在使用时,就会把this当做该对象。
// "this" 隐式具有类型 "any",因为它没有类型注释。
// 此容器隐藏了 "this" 的外部值。
function eating() {
console.log(this.name);
}
const info = {
name: "why",
eating: eating,
};
// 隐式绑定
info.eating();
这段代码运行会报错的:
- TypeScript进行类型检测的目的是让代码更加的安全;
- 所以这里对于 eating 的调用来说,虽然将其放到了info中,通过info去调用,this依然是指向info对象的;
- 但是对于TypeScript编译器来说,这个代码是非常不安全的,因为
也有可能直接调用函数,或者通过别的对象来调用函数;
这个时候,通常TypeScript会要求明确的指定this的类型:
type ThisType = { name: string };
//这里的this如果要传,必须是第一个位置,且他是作为类型注解而不是参数
function eating(this: ThisType, message: string) {
// 如果没有this注解,那么这里的this指向any
console.log(this.name + " eating", message);
}
const info = {
name: "why",
eating: eating,
};
// 隐式绑定
info.eating("哈哈哈");
// 显示绑定
eating.call({name: "kobe"}, "呵呵呵")
eating.apply({name: "james"}, ["嘿嘿嘿"])
# 匿名函数的参数
匿名函数与函数声明会有一些不同:
- 当一个函数出现在TypeScript可以确定该函数会被如何调用的地方时;
- 该函数的参数会自动指定类型;
const names = ["abc", "cba", "nba"]
// item根据上下文的环境推导出来的, 这个时候可以不添加的类型注解
// 上下文中的函数: 可以不添加类型注解
names.forEach(function(item) {
console.log(item.split(""))
})
这个过程称之为上下文类型(contextual typing),因为函数执行的上下文可以帮助确定参数和返回值的类型;
# 调用签名
在 JavaScript 中,函数除了可以被调用,自己也是可以有属性值的。函数类型表达式并不能直接支持声明属性,如果想描述一个带有属性的函数,可以在一个对象类型中写一个调用签名(call signature)。
- 回调签名
//给函数添加属性,要通过对象的方式来定义
type DescribableFunction ={
//在对象里定义一个属性名字discription,并给它定义一个string类型
description:string
//再定义一个函数体 (函数参数:类型):返回值的类型
//注意:在参数列表和返回类型之间使用:而不是=>
(someArg:number):boolean
}
//定义一个doSomething函数调用函数参数,业务代码处理部分
export function doSomething(fn:DescribableFunction){
//调用函数属性和函数本身(函数要传递一个number类型,这里传入一个6)
console.log(fn.description + 'return' + fn(6));
}
//doSomething需要传入一个参数fn,这里定义一个fn1将参数传给doSomething()
function fn1(n:number){
console.log(n);
//这里要返回一个boolean类型
return true
}
//那么fn1就可以绑定属性传参了,注意这里绑定的属性必须是type DescribableFunction{}对象里定义过的属性
fn1.description='hello'
//doSomething就可以将一个function传入了,fn1满足了返回的类型
doSomething(fn1)
注意这个语法跟函数类型表达式稍有不同,在参数列表和返回的类型之间用的是 : 而不是 =>
- 构造签名
export class Ctor {
//类里顶一个参数s类型sting
s: string
//再定义一个构造函数,这个函数需要传入一个s参数string类型,然后把参数s赋值给this.s
constructor(s: string) {
this.s = s
}
}
//1.通过type来定义一个构造签名,取名为SomeConstructor,后面是对象
type SomeConstructor = {
//在对象里先定义一个调用签名 格式:(函数的参数:参数的类型):返回的类型/构造的类
new (s: string): Ctor //这是一个够咱函数的签名
// new 关键字表示这是一个构造函数
// (s: string) 构造函数的 参数列表,表示这个构造函数 接受一个类型为string的参数s
// :Ctor是构造函数的返回类型
}
//3.使用函数 参数为Ctor:类型为type定义好的类型名:SomeConstructor
function Fn(Ctor: SomeConstructor) {
//Ctor: SomeConstructor可以理解为一个构造函数,那么这里就可以调用这个函数了,Ctor('字符串'),
//但是这里只实现了调用签名,并没有实现构造签名,在ts里要实现构造签名,需要在type SomeConstructor={}对象里的方法添加一个new,
//这样它就是一个构造函数类型了,那么在这里的Ctor('字符串')前面也要加一个new才能够被实例化。如果要返回这个实例,
//前面再加一个return,那么这里就返回了一个构造函数的实例了
return new Ctor('字符串');
}
//4.调用函数,传入的参数是构造函数类型,这里传入Ctor
const fn = Fn(Ctor)
//5.打印,运行 node 05-constructor.js
console.log(fn.s);
有的对象,比如JavaScript的Date对象,可以在有 new或者没有new的情况下,都可以被调用,那我们可以在同一个类型中,任意地结合调用和构造签名
//1.定义一个接口,名字CallOrConstructor,可以调用也可以构造的意思
interface CallOrConstructor {
//(2)再构造一个函数,返回一个Date类型。这叫构造签名的方式
new(s: string): Date
//(1)定义一个函数体,传参为可选型。这叫调用签名的方式
(n?: number): number
}
//2.使用接口类型
export function Fn(date: CallOrConstructor) {
//使用构造签名函数,new实例化date再传入字符串参数
let d = new date('2000-10-2')
//使用调用签名
let n = date(100)
}
//以上,实现了结合使用调用签名和构造签名 两种方法 去定义了一个类型
# 泛型函数
通过给函数添加一个类型参数 Type,并且在两个地方使用它,我们就在函数的输入(即数组)和函数的输出(即返回值)之间创建了一个关联。现在当我们调用它,一个更具体的类型就会被判断出来
function firstElement<Type>(arr: Type[]): Type | undefined {
return arr[0];
}
// s is of type 'string'
const s = firstElement(["a", "b", "c"]);
// n is of type 'number'
const n = firstElement([1, 2, 3]);
// u is of type undefined
const u = firstElement([]);
推论:这里没有明确指定 Type 的类型,类型是被 TypeScript 自动推断出来的
function map<Input, Output>(arr: Input[], func: (arg: Input) => Output): Output[] {
return arr.map(func);
}
// Parameter 'n' is of type 'string'
// 'parsed' is of type 'number[]'
const parsed = map(["1", "2", "3"], (n) => parseInt(n));
console.log(parsed)
# 函数约束 extends
有的时候,想关联两个值,但只能操作值的一些固定字段,这种情况,可以使用 约束(constraint) 对类型参数进行限制。
写一个函数,函数返回两个值中更长的那个。为此,我们需要保证传入的值有一个 number 类型的 length 属性。使用 extends 语法来约束函数参数:
function longest<Type extends { length: number }>(a: Type, b: Type) {
if (a.length >= b.length) {
return a;
} else {
return b;
}
}
// longerArray is of type 'number[]'
const longerArray = longest([1, 2], [1, 2, 3]);
// longerString is of type 'alice' | 'bob'
const longerString = longest("alice", "bob");
// Error! Numbers don't have a 'length' property
const notOK = longest(10, 100);
// 类型“number”的参数不能赋给类型“{ length: number; }”的参数。
基于传入的参数,longerArray和 longerString 中的类型都被推断出来了。记住,所谓泛型就是用一个相同类型来关联两个或者更多的值。
function minimumLength<Type extends { length: number }>(
obj: Type,
minimum: number
): Type {
if (obj.length >= minimum) {
return obj;
} else {
// obj不一定仅仅只有length属性
return { length: minimum };
// Type '{ length: number; }' is not assignable to type 'Type'.
// '{ length: number; }' is assignable to the constraint of type 'Type', but 'Type' could be instantiated with a different subtype of constraint '{ length: number; }'.
}
}
这个函数看起来像是没有问题,Type 被 { length: number} 约束,函数返回 Type 或者一个符合约束的值。
而这其中的问题就在于函数理应返回与传入参数相同类型的对象,而不仅仅是符合约束的对象。我们可以写出这样一段反例:
// 'arr' gets value { length: 6 }
const arr = minimumLength([1, 2, 3], 6);
// and crashes here because arrays have
// a 'slice' method, but not the returned object!
console.log(arr.slice(0));
- 如果不需要约束的其实可以不约束
function firstElement1<Type>(arr: Type[]) {
return arr[0];
}
function firstElement2<Type extends any[]>(arr: Type) {
return arr[0];
}
// a: number (good)
const a = firstElement1([1, 2, 3]);
// b: any (bad)
const b = firstElement2([1, 2, 3]);
# 可选参数和默认参数
function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
let result1 = buildName("Bob"); // 正确
let result2 = buildName("Bob", "Adams", "Sr."); // 错误,参数太多了
let result3 = buildName("Bob", "Adams"); // 正确
// 默认参数:这样第二个参数可传可不传
function calculate_discount(price:number,rate:number = 0.50) {
var discount = price * rate;
console.log("计算结果: ",discount);
}
- 剩余参数:函数的最后一个命名参数 restOfName 以 ... 为前缀,它将成为一个由剩余参数组成的数组,索引值从0(包括)到 restOfName.length(不包括)。
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
# 重载函数
编译器并不知道入参是什么类型的,返回值类型也不能确定。这时可以为同一个函数提供多个函数类型定义来进行函数重载。
function reverse(x: string): string
function reverse(x: number): number
function reverse(target: string | number) {
if (typeof target === 'string') {
return target.split('').reverse().join('')
}
if (typeof target === 'number') {
return +[...target.toString()].reverse().join('')
}
}
console.log(reverse('imooc')) // coomi
console.log(reverse(23874800)) // 847832
在这个例子中,写了两个函数重载,一个接受一个参数,另外一个接受三个参数。前面两个函数签名被称为重载签名 (overload signatures)。
然后,写了一个兼容签名的函数实现,称之为实现签名 (implementation signature) ,但这个签名不能被直接调用。尽管在函数声明中,在一个必须参数后,声明了两个可选参数,它依然不能被传入两个参数进行调用。
function makeDate(timestamp: number): Date;
function makeDate(m: number, d: number, y: number): Date;
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
if (d !== undefined && y !== undefined) {
return new Date(y, mOrTimestamp, d);
} else {
return new Date(mOrTimestamp);
}
}
const d1 = makeDate(12345678);
const d2 = makeDate(5, 5, 5);
const d3 = makeDate(1, 3);
// 没有需要 2 参数的重载,但存在需要 1 或 3 参数的重载。
- 函数重载注意签名的兼容性和实现
function fn(x: boolean): void;
// Argument type isn't right
function fn(x: string): void;//此重载签名与其实现签名不兼容。
function fn(x: boolean) {}
function fn(x: string): string;
// Return type isn't right
function fn(x: number): boolean;//此重载签名与其实现签名不兼容。
function fn(x: string | number) {
return "oops";
}
function fn(x: string): void;
function fn() {
// ...
}
// 应有 1 个参数,但获得 0 个。ts(2554) 01.ts(1, 13): 未提供 "x" 的自变量。
fn();
// 函数的重载: 函数的名称相同, 但是参数不同的几个函数, 就是函数的重载
function add(num1: number, num2: number): number; // 没函数体
function add(num1: string, num2: string): string;
function add(num1: any, num2: any): any {
if (typeof num1 === 'string' && typeof num2 === 'string') {
return num1.length + num2.length
}
return num1 + num2
}
const result = add(20, 30)
const result2 = add("abc", "cba")
console.log(result)
console.log(result2)
// 在函数的重载中, 实现函数是不能直接被调用的
// add({name: "why"}, {age: 18})
export {}
# 函数剩余参数问题
as const 也是类型断言的一种 这被称为const断言。const断言告诉编译器为表达式推断出它能推断出的最窄或最特定的类型。如果不使用它,编译器将使用其默认类型推断行为,这可能会导致更广泛或更一般的类型。
请注意,它被称为“断言”,而不是“强制转换”。
const args = [8, 5];
// const args: number[]
const angle = Math.atan2(...args); // error! Expected 2 arguments, but got 0 or more.
console.log(angle);
编译器看到const args = [8, 5];,并推断出number[]的类型。这是一个由0个或更多number类型的元素组成的可变数组。编译器不知道有多少或哪些元素。这样的推断通常是合理的;通常,数组内容意味着要以某种方式进行修改。如果有人想写args.push(17)或args[0]++,他们会很乐意使用number[]类型。
不幸的是,下一行Math.atan2(…args)导致了一个错误。Math.atan2()函数正好需要两个数值参数。但是编译器只知道args是一个数字数组。它完全忘记了有两个元素,因此编译器抱怨说,当它只需要两个参数时,您正在用“0或更多”参数调用Math.atan2()。
将其与as const的代码进行比较:
const args = [8, 5] as const;
// const args: readonly [8, 5]
const angle = Math.atan2(...args); // okay
console.log(angle);
现在编译器推断args属于readonly [8, 5]类型。。。一个readonly元组,其值正好是按此顺序排列的数字8和5。具体来说,args.length被编译器精确地称为2。
这就足够下一行使用Math.atan2()了。编译器知道Math.atan2(…args)与Math.atan2(8, 5)相同,这是一个有效的调用。
- tsconfig.json :2021年默认生成的target是es5,会导致上述代码运行错误:Type 'string' is not an array type.需要开启es2015以上来修改
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
"module": "commonjs",
"downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring whe
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}
# 写一个好的函数重载的一些建议
这个函数代码功能实现了,也没有什么报错,但我们不能传入一个可能是字符串或者是数组的值,因为 TypeScript 只能一次用一个函数重载处理一次函数调用。
function len(s: string): number;
function len(arr: any[]): number;
function len(x: any) {
return x.length;
}
len(""); // OK
len([0]); // OK
len(Math.random() > 0.5 ? "hello" : [0]);
// 没有与此调用匹配的重载。
// 第 1 个重载(共 2 个),“(s: string): number”,出现以下错误。
// 类型“number[] | "hello"”的参数不能赋给类型“string”的参数。
// 不能将类型“number[]”分配给类型“string”。
// 第 2 个重载(共 2 个),“(arr: any[]): number”,出现以下错误。
// 类型“number[] | "hello"”的参数不能赋给类型“any[]”的参数。
// 不能将类型“string”分配给类型“any[]”。ts(2769)
// 01.ts(3, 10): 针对此实现的调用已成功,但重载的实现签名在外部不可见。
因为两个函数重载都有相同的参数数量和相同的返回类型,我们可以写一个无重载版本的函数替代:
function len(x: any[] | string) {
return x.length;
}
这样函数就可以传入两个类型中的任意一个了。
尽可能的使用联合类型替代重载
- ts使用this作为参数
//函数内This 的声明
interface User {
admin: boolean
}
interface DB {
filterUsers(filter: (this: User) => boolean): User[];
}
const db:DB = {
filterUsers: (filter: (this: User) => boolean) => {
let user1 = { admin: true }
let user2 = { admin: false }
return [user1, user2]
}
}
//这里不能使用箭头函数
const admins = db.filterUsers(function (this: User) {
return this.admin;
})
console.log(admins)
注意需要使用 function 的形式而不能使用箭头函数
# 函数声明法
函数声明法创建函数是最常用的函数定义法。使用function关键字和函数名去定义一个函数。
function add(n1:number,n2:number):number{
return n1+n2
}
# 函数表达式法
函数表达式法是将一个函数赋值给一个变量,这个变量名就是函数名。通过变量名就可以调用函数了。这种方式定义的函数,必须在定义之后,调用函数。下面例子中等号右边的函数没有函数名,称为匿名函数。
var add = function(n1:number,n2:number):number{
return n1+n2
}
console.log(add(1,4))
const func1: (str: string) => number = str => {
return parseInt(str, 10);
};
func1的函数声明
这里的 (str: string) => number中的=>实际上是返回的意思而不是箭头函数的中的箭头的作用
# 箭头函数
var add = (n1:number,n2:number):number=>{
return n1+n2
}
console.log(add(1,4))
函数返回值关键字可以是number string之类的也可以无返回值void或者永远不能全部执行结束的never类型
function errorEmitter(): never {
while(true) {}
}
还可以使用接口的方式进行定义
interface Isum{
(age:number,time?:number):number;
}
const fun:Isum=(a,b)=>{
return a+b
}