# 函数
和 JavaScript 一样,TypeScript 函数可以创建有名字的函数和匿名函数,但是在此基础上 Typescript 支持函数重载。
TypeScript 里的每个函数参数都是必须的。 这不是指不能传递 null
或 undefined
作为参数,而是说编译器检查用户是否为每个参数都传入了值。编译器还会假设只有这些参数会被传递进函数。 简短地说,传递给一个函数的参数个数必须与函数期望的参数个数一致。
JavaScript 里,每个参数都是可选的,可传可不传。 没传参的时候,它的值就是 undefined
。 在 TypeScript 里我们可以在参数名旁使用 ?
实现可选参数的功能。 比如,我们想让 name
是可选的:
function fn(name?:string):void{ | |
console.log(name ? name : 'asuhe') | |
} |
# 剩余参数
必要参数,默认参数和可选参数有个共同点:它们表示某一个参数。 有时,你想同时操作多个参数,或者你并不知道会有多少参数传递进来。 在 JavaScript 里,你可以使用 arguments
来访问所有传入的参数。
在 TypeScript 里,你可以把所有参数收集到一个变量里:
剩余参数会被当做个数不限的可选参数。 可以一个都没有,同样也可以有任意个。 编译器创建参数数组,名字是你在省略号( ...
)后面给定的名字,你可以在函数体内使用这个数组。
function info(x: string, ...args: string[]) { | |
console.log(x, args) | |
} | |
info('abc', 'c', 'b', 'a') |
# 函数重载
函数重载:函数名相同,而形参不同的多个函数
在 JS 中,由于弱类型的特点和形参与实参可以不匹配,是没有函数重载这一说的 但在 TS 中,与其它面向对象的语言 (如 Java) 就存在此语法
/* | |
函数重载:函数名相同,而形参不同的多个函数 | |
需求:我们有一个 add 函数,它可以接收 2 个 string 类型的参数进行拼接,也可以接收 2 个 number 类型的参数进行相加 | |
*/ | |
// 重载函数声明 | |
function add (x: string, y: string): string | |
function add (x: number, y: number): number | |
// 定义函数实现 | |
function add(x: string | number, y: string | number): string | number { | |
// 在实现上我们要注意严格判断两个参数的类型是否相等,而不能简单的写一个 x + y | |
if (typeof x === 'string' && typeof y === 'string') { | |
return x + y | |
} else if (typeof x === 'number' && typeof y === 'number') { | |
return x + y | |
} | |
} | |
console.log(add(1, 2)) | |
console.log(add('a', 'b')) | |
// console.log(add(1, 'a')) // error |
# 命名空间
Typescript
还支持类似于 C++
的命名空间,它最主要的作用就是防止变量冲突。每个命名空间都必须处于顶级作用域内,命名空间内的变量或函数需要对外暴露时仅需要在前面加上 export
关键字。
// index.ts | |
namespace K { | |
export const name = "aushe" | |
} | |
namespace N { | |
export const name = "sphinx" | |
} | |
console.log(K.name) // asuhe | |
consoe.log(N.name) // sphinx |
# 泛型
泛型指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型的一种特性。
下面创建一个函数,实现功能:根据指定的数量 count
和数据 value
, 创建一个包含 count
个 value
的数组 不用泛型的话,这个函数可能是下面这样:
function createArray(value: any, count: number): any[] { | |
const arr: any[] = []; | |
for (let i = 0; i < count; i++) { | |
arr.push(value); | |
} | |
return arr; | |
} | |
const arr = createArray('asuhe',5); | |
console.log(arr[0].split('')) // 不会提示 split 函数,编辑器无法推断 arr [0] 就是 string |
但我们如果使用泛型,那么就可以解决这个问题。代码如下:
function createArray<T>(value: T, count: number){ | |
const arr: Array<T> = []; | |
for (let i = 0; i < count; i++) { | |
arr.push(value); | |
} | |
return arr; | |
} | |
const arr = createArray('asuhe',5); | |
console.log(arr[0].split('')) // 能够提示 split 函数可以使用 |
一个函数还可以定义多个泛型参数,泛型参数的命名同其它变量一样,但是惯例来说需要大写且仅一个字母:
function swap <K, V> (a: K, b: V): [K, V] { | |
return [a, b] | |
} | |
const result = swap<string, number>('abc', 123) | |
console.log(result[0].length, result[1].toFixed()) |
# 泛型接口
在定义接口时,为接口中的属性或方法定义泛型类型
在使用接口时,再指定具体的泛型类型
interface IbaseCRUD <T> { | |
data: T[] | |
add: (t: T) => void | |
getById: (id: number) => T | |
} | |
class User { | |
id?: number; //id 主键自增 | |
name: string; // 姓名 | |
age: number; // 年龄 | |
constructor (name, age) { | |
this.name = name | |
this.age = age | |
} | |
} | |
class UserCRUD implements IbaseCRUD <User> { | |
data: User[] = [] | |
add(user: User): void { | |
user = {...user, id: Date.now()} | |
this.data.push(user) | |
console.log('保存user', user.id) | |
} | |
getById(id: number): User { | |
return this.data.find(item => item.id===id) | |
} | |
} | |
const userCRUD = new UserCRUD() | |
userCRUD.add(new User('tom', 12)) | |
userCRUD.add(new User('tom2', 13)) | |
console.log(userCRUD.data) |
# 泛型类
在定义类时,为类中的属性或方法定义泛型类型 在创建类的实例时,再指定特定的泛型类型
class GenericNumber<T> { | |
zeroValue: T | |
add: (x: T, y: T) => T | |
} | |
let myGenericNumber = new GenericNumber<number>() | |
myGenericNumber.zeroValue = 0 | |
myGenericNumber.add = function(x, y) { | |
return x + y | |
} | |
let myGenericString = new GenericNumber<string>() | |
myGenericString.zeroValue = 'abc' | |
myGenericString.add = function(x, y) { | |
return x + y | |
} | |
console.log(myGenericString.add(myGenericString.zeroValue, 'test')) | |
console.log(myGenericNumber.add(myGenericNumber.zeroValue, 12)) |
# 泛型约束
如果我们直接对一个泛型参数取 length
属性,会报错,因为这个泛型根本就不知道它有这个属性
// 没有泛型约束 | |
function fn <T>(x: T): void { | |
console.log(x.length) // error | |
} |
我们可以使用泛型约束来实现
interface Lengthwise { | |
length: number; | |
} | |
// 指定泛型约束 | |
function fn2 <T extends Lengthwise>(x: T): void { | |
console.log(x.length) | |
} | |
// 我们需要传入符合约束类型的值,必须包含必须 `length` 属性: | |
fn2('abc') | |
fn2(123) //error number 没有 length 属性 |