## **TypeScript中的基本类型**
TypeScript中的基本类型:
- 类型声明
- 类型声明是TS非常重要的一个特点;
- 通过类型声明可以指定TS中变量(参数、形参)的类型;
- 指定类型后,当为变量赋值时,TS编译器会自动检查值是否符合类型声明,符合则赋值,否则报错;
- 简而言之,类型声明给变量设置了类型,使得变量只能存储某种类型的值;
- 语法:
- ```typescript
let 变量: 类型;
let 变量: 类型 = 值;
function fn(参数: 类型, 参数: 类型): 类型{
...
}
```
- 自动类型判断
- TS拥有自动的类型判断机制
- 当对变量的声明和赋值是同时进行的,TS编译器会自动判断变量的类型
- 所以如果你的变量的声明和赋值时同时进行的,可以省略掉类型声明
- 类型:
| **类型** | **例子** | **描述** |
| :------: | :---------------: | :----------------------------: |
| number | 1, -33, 2.5 | 任意数字 |
| string | 'hi', "hi", `hi` | 任意字符串 |
| boolean | true、false | 布尔值true或false |
| 字面量 | 其本身 | 限制变量的值就是该字面量的值 |
| any | * | 任意类型 |
| unknown | * | 类型安全的any |
| void | 空值(undefined) | 没有值(或undefined) |
| never | 没有值 | 不能是任何值 |
| object | {name:'孙悟空'} | 任意的JS对象 |
| array | [1,2,3] | 任意JS数组 |
| tuple | [4,5] | 元素,TS新增类型,固定长度数组 |
| enum | enum{A, B} | 枚举,TS中新增类型 |
- number
- ```typescript
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
let big: bigint = 100n;
```
- boolean
- ```typescript
let isDone: boolean = false;
```
- string
- ```typescript
let color: string = "blue";
color = 'red';
let fullName: string = `Bob Bobbington`;
let age: number = 37;
let sentence: string = `Hello, my name is ${fullName}.
I'll be ${age + 1} years old next month.`;
```
- 字面量
- 也可以使用字面量去指定变量的类型,通过字面量可以确定变量的取值范围
- ```typescript
let color: 'red' | 'blue' | 'black';
let num: 1 | 2 | 3 | 4 | 5;
```
- any
- ```typescript
let d: any = 4;
d = 'hello';
d = true;
```
- unknown
- ```typescript
let notSure: unknown = 4;
notSure = 'hello';
```
- void
- ```typescript
let unusable: void = undefined;
```
- never
- ```typescript
function error(message: string): never {
throw new Error(message);
}
```
- object(没啥用)
- ```typescript
let obj: object = {};
```
- array
- ```typescript
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];
```
- tuple
- ```typescript
let x: [string, number];
x = ["hello", 10];
```
- enum
- ```typescript
enum Color {
Red,
Green,
Blue,
}
let c: Color = Color.Green;
enum Color {
Red = 1,
Green,
Blue,
}
let c: Color = Color.Green;
enum Color {
Red = 1,
Green = 2,
Blue = 4,
}
let c: Color = Color.Green;
```
- 类型断言
- 有些情况下,变量的类型对于我们来说是很明确,但是TS编译器却并不清楚,此时,可以通过类型断言来告诉编译器变量的类型,断言有两种形式:
- 第一种
- ```typescript
let someValue: unknown = "this is a string";
let strLength: number = (someValue as string).length;
```
- 第二种
- ```typescript
let someValue: unknown = "this is a string";
let strLength: number = (<string>someValue).length;
```
/* 1.变量类型声明和初始化 */
let a: number;
// a 的类型设置为了number,在以后的使用过程中a的值只能是数字
a = 10;
a = 33;
// a = 'hello'; // 此行代码会报错,因为变量a的类型是number,不能赋值字符串
let b: string;
b = 'hello';
// b = 123;
// 声明变量的同时直接进行赋值
// let c: boolean = false;
// 如果变量的声明和赋值是同时进行的,TS可以自动对变量进行类型检测
let c = false;
c = true;
/* 2.函数声明 */
// JS中的函数是不考虑参数的类型和个数的
// function sum(a, b){
// return a + b;
// }
// console.log(sum(123, 456)); // 579
// console.log(sum(123, "456")); // "123456"
function sum(a: number, b: number): number {
return a + b;
}
let res = sum(123, 345);
// let res = sum(123, '456'); // 报错
/* 3.字面量声明 */
// 下面的语句相当于定义了一个a3常量;
let a3: 10;
// a3 = 11; // 尝试将a3赋值为11,报错;
/* 4.联合类型 */
let b4: "male" | "female";
b4 = "male";
b4 = "female";
let c4: boolean | string;
c4 = true;
c4 = 'hello';
/* 5.组合类型 */
// &表示同时满足的类型
let a5: { name: string } & { age: number };
a5 = {name: 'haha', age: 18};
/* 6.其他常见基本类型(除了:number、string、bool之外) */
/* 6.1 Any */
// any 表示的是任意类型,一个变量设置类型为any后相当于:对该变量关闭了TS的类型检测!
// 使用TS时,强烈不建议使用any类型
// let d: any;
// 声明变量如果不指定类型,则TS解析器会自动判断变量的类型为any (隐式的any)
let a61;
a61 = 10;
a61 = 'hello';
a61 = true;
/* 6.2 Unknown */
let a62: unknown;
a62 = 10;
a62 = 'hello';
a62 = true;
/* 6.2.2 Unknown和Any对比 */
let s: string = 'hello';
// a61的类型是any,它可以赋值给任意变量
// any赋值给其他变量时,TS也会同时关闭对那个赋值变量的类型检查!
s = a61;
// a62的类型是unknown,它不能赋值给一个确定类型!
// s = a62;
// 即:unknown 实际上就是一个类型安全的any
// unknown类型的变量,不能直接赋值给其他变量
if (typeof a62 === "string") {
s = a62;
}
/* 6.3 void */
// void 用来表示空,主要用于函数中
// 以函数为例,表示没有返回值的函数:
function fn(): void {
}
/* 6.4 undefined */
// never 表示永远不会返回结果(连undefined都没有)[较少使用]
// 下面的函数没有返回值(连undefined都没有),仅抛出error
function fn2(): never {
throw new Error('报错了!');
}
/* 6.5 object */
// object表示一个js对象类型
let a65: object;
a65 = {};
a65 = function () {
};
// {} 用来指定对象中可以包含哪些属性
// 语法:
// {属性名:属性值,属性名:属性值}
// 在属性名后边加上?,表示属性是可选的
let b65: { name: string, age?: number };
b65 = {name: 'haha'};
b65 = {name: '孙悟空', age: 18};
// [propName: string]: any 表示任意类型的属性
// 所以下面定义了一个必须包含name属性,其他属性随意的类型
let c65: { name: string, [propName: string]: any };
c65 = {name: '猪八戒', age: 18, gender: '男'};
/* 6.5.2 函数对象 */
/*
* 设置函数结构的类型声明:
* 语法:(形参:类型, 形参:类型 ...) => 返回值
*/
let d65: (a: number, b: number) => number;
// 声明了类型之后,在定义时可以不再指出类型
d65 = function (n1, n2) {
return n1 + n2
}
// 也可以指出类型
d65 = function (n1: number, n2: number): number {
return n1 + n2
}
// 下面的一些定义都是错误的!
// d65 = function (n1: string, n2: number): number {
// return n1 + n2
// }
// d65 = function (n1: number, n2: number, n3: number): number {
// return n1 + n2 + n3;
// }
// 但是下面这个是对的!
d65 = function (n1: number): number {
return n1
}
/* 6.6 数组 */
/*
* 数组的类型声明:
* 类型[]
* Array<类型>
*/
// 例如:string[] 表示字符串数组
let e66: string[];
e66 = ['a', 'b', 'c'];
// number[] 表示数值数值
let f66: number[];
// 也可以使用下面的声明方式
let g66: Array<number>;
g66 = [1, 2, 3];
/* 6.7 元组 */
/*
* 元组,元组就是固定长度的数组
* 语法:[类型, 类型, 类型]
* 相比于数组,元组的性能更高一些
*/
let a67: [string, number];
a67 = ['hello', 123];
/* 6.8 枚举 */
enum Gender {
Male,
Female,
}
let a68: { name: string, gender: Gender };
a68 = {
name: 'hello',
gender: Gender.Male
}
console.log(a68.gender === Gender.Male);
/* 7.类型别名 */
// 使用关键字type
type myType = 1 | 2 | 3 | 4 | 5;
let k: myType;
let l: myType;
let m: myType;
k = 2;
// k = 6 // error!
/* 8.类型断言 */
// 类型断言,用来帮助编译器判断变量的实际类型
/*
* 语法:
* 变量 as 类型
* <类型>变量
*
* */
// 有些情况下,变量的类型对于我们来说是很明确
// 但是TS编译器却并不清楚
// 此时,可以通过类型断言来告诉编译器变量的类型
// 断言有两种形式:
// 8.1:变量 as 类型
let someValue: unknown = "this is a string";
let strLength: number = (someValue as string).length;
// 8.2:<类型>变量
let strLength2: number = (<string>someValue).length;
## 面向对象
要想面向对象,操作对象,首先便要拥有对象;
要创建对象,必须要先定义类,所谓的类可以理解为对象的模型;
程序中可以根据类创建指定类型的对象;
举例来说:
可以通过Person类来创建人的对象,通过Dog类创建狗的对象,不同的类可以用来创建不同的对象;
### 定义类
```typescript
class 类名 {
属性名: 类型;
constructor(参数: 类型){
this.属性名 = 参数;
}
方法名(){
....
}
}
示例:
class Person{
name: string;
age: number;
constructor(name: string, age: number){
this.name = name;
this.age = age;
}
sayHello(){
console.log(`大家好,我是${this.name}`);
}
}
使用类:
const p = new Person('孙悟空', 18);
p.sayHello();
构造函数
可以使用constructor
定义一个构造器方法;
注1:在TS中只能有一个构造器方法!
例如:
class C{
name: string;
age: number
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
同时也可以直接将属性定义在构造函数中:
class C {
constructor(public name: string, public age: number) {
}
}
上面两种定义方法是完全相同的!
注2:子类继承父类时,必须调用父类的构造方法(如果子类中也定义了构造方法)!
例如:
class A {
protected num: number;
constructor(num: number) {
this.num = num;
}
}
class X extends A {
protected name: string;
constructor(num: number, name: string) {
super(num);
this.name = name;
}
}
如果在X类中不调用super
将会报错!
封装
对象实质上就是属性和方法的容器,它的主要作用就是存储属性和方法,这就是所谓的封装
默认情况下,对象的属性是可以任意的修改的,为了确保数据的安全性,在TS中可以对属性的权限进行设置
-
静态属性(static):
- 声明为static的属性或方法不再属于实例,而是属于类的属性;
-
只读属性(readonly):
- 如果在声明属性时添加一个readonly,则属性便成了只读属性无法修改
-
TS中属性具有三种修饰符:
- public(默认值),可以在类、子类和对象中修改
- protected ,可以在类、子类中修改
- private ,可以在类中修改
示例:
public:
class Person{
public name: string; // 写或什么都不写都是public
public age: number;
constructor(name: string, age: number){
this.name = name; // 可以在类中修改
this.age = age;
}
sayHello(){
console.log(`大家好,我是${this.name}`);
}
}
class Employee extends Person{
constructor(name: string, age: number){
super(name, age);
this.name = name; //子类中可以修改
}
}
const p = new Person('孙悟空', 18);
p.name = '猪八戒';// 可以通过对象修改
protected:
class Person{
protected name: string;
protected age: number;
constructor(name: string, age: number){
this.name = name; // 可以修改
this.age = age;
}
sayHello(){
console.log(`大家好,我是${this.name}`);
}
}
class Employee extends Person{
constructor(name: string, age: number){
super(name, age);
this.name = name; //子类中可以修改
}
}
const p = new Person('孙悟空', 18);
p.name = '猪八戒';// 不能修改
private:
class Person{
private name: string;
private age: number;
constructor(name: string, age: number){
this.name = name; // 可以修改
this.age = age;
}
sayHello(){
console.log(`大家好,我是${this.name}`);
}
}
class Employee extends Person{
constructor(name: string, age: number){
super(name, age);
this.name = name; //子类中不能修改
}
}
const p = new Person('孙悟空', 18);
p.name = '猪八戒';// 不能修改
属性存取器
对于一些不希望被任意修改的属性,可以将其设置为private
直接将其设置为private将导致无法再通过对象修改其中的属性
我们可以在类中定义一组读取、设置属性的方法,这种对属性读取或设置的属性被称为属性的存取器
读取属性的方法叫做setter方法,设置属性的方法叫做getter方法
示例:
class Person{
private _name: string;
constructor(name: string){
this._name = name;
}
get name(){
return this._name;
}
set name(name: string){
this._name = name;
}
}
const p1 = new Person('孙悟空');
// 实际通过调用getter方法读取name属性
console.log(p1.name);
// 实际通过调用setter方法修改name属性
p1.name = '猪八戒';
静态属性
静态属性(方法),也称为类属性。使用静态属性无需创建实例,通过类即可直接使用
静态属性(方法)使用static开头
示例:
class Tools{
static PI = 3.1415926;
static sum(num1: number, num2: number){
return num1 + num2
}
}
console.log(Tools.PI);
console.log(Tools.sum(123, 456));
this
在类中,使用this表示当前对象
继承
继承时面向对象中的又一个特性
通过继承可以将其他类中的属性和方法引入到当前类中
示例:
class Animal{
name: string;
age: number;
constructor(name: string, age: number){
this.name = name;
this.age = age;
}
}
class Dog extends Animal{
bark(){
console.log(`${this.name}在汪汪叫!`);
}
}
const dog = new Dog('旺财', 4);
dog.bark();
通过继承可以在不修改类的情况下完成对类的扩展
重写
发生继承时,如果子类中的方法会替换掉父类中的同名方法,这就称为方法的重写
示例:
class Animal{
name: string;
age: number;
constructor(name: string, age: number){
this.name = name;
this.age = age;
}
run(){
console.log(`父类中的run方法!`);
}
}
class Dog extends Animal{
bark(){
console.log(`${this.name}在汪汪叫!`);
}
run(){
console.log(`子类中的run方法,会重写父类中的run方法!`);
}
}
const dog = new Dog('旺财', 4);
dog.bark();
在子类中可以使用super来完成对父类的引用
抽象类(abstract class)
抽象类是专门用来被其他类所继承的类,它只能被其他类所继承不能用来创建实例
abstract class Animal{
abstract run(): void;
bark(){
console.log('动物在叫~');
}
}
class Dog extends Animals{
run(){
console.log('狗在跑~');
}
}
使用abstract开头的方法叫做抽象方法,抽象方法没有方法体只能定义在抽象类中,继承抽象类时抽象方法必须要实现;
// 使用class关键字来定义一个类
/*
* 对象中主要包含了两个部分:
* 属性
* 方法
*/
class Person {
/*
* 直接定义的属性是实例属性,需要通过对象的实例去访问:
* const per = new Person();
* per.name
*
* 使用static开头的属性是静态属性(类属性),可以直接通过类去访问
* Person.age
*
* readonly开头的属性表示一个只读的属性无法修改
*/
/*
定义实例属性(new出实例之后才能访问的属性)
*/
// name: string = '孙悟空';
// age:number = 18;
/*
定义静态属性:在属性前使用static关键字可以定义类属性(静态属性)
*/
// static age: number = 18;
/*
readonly开头的属性表示一个只读的属性无法修改
*/
// readonly name: string = '孙悟空';
name: string = '孙悟空';
age:number = 18;
/*
* 定义方法:如果方法以static开头则方法就是类方法,可以直接通过类去调用
*/
// 实例方法
sayHello(){
console.log('Hello 大家好!');
}
// 类方法
static sayHello(){
console.log('Hello 大家好!');
}
}
/* 创建实例 */
const per = new Person();
/* 属性 */
// 访问实例属性
console.log(per);
console.log(per.name, per.age);
// 访问静态(类)属性
// console.log(Person.age);
// 如果是readonly类型,则无法修改
// 下面的代码报错
// per.name = 'tom';
/* 方法 */
// 访问实例方法
per.sayHello();
// 访问静态方法
Person.sayHello();
class Dog {
name: string;
age: number;
// constructor 被称为构造函数
// 构造函数会在对象创建时调用
// TS中仅能创建一个构造函数!
constructor(name: string, age: number) {
// 在实例方法中,this就表示当前当前的实例
// 在构造函数中的当前对象就是新建的那个对象
// 可以通过this向新建的对象中添加属性
this.name = name;
this.age = age;
}
bark() {
// alert('汪汪汪!');
// 在方法中可以通过this来表示当前调用方法的对象
console.log(this.name);
}
}
const dog = new Dog('小黑', 4);
const dog2 = new Dog('小白', 2);
console.log(dog);
console.log(dog2);
dog2.bark();
(function () {
// 定义一个Animal类
class Animal {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayHello() {
console.log('动物在叫~');
}
}
/*
* Dog extends Animal
* - 此时,Animal被称为父类,Dog被称为子类
* - 使用继承后,子类将会拥有父类所有的方法和属性
* - 通过继承可以将多个类中共有的代码写在一个父类中,
* 这样只需要写一次即可让所有的子类都同时拥有父类中的属性和方法
* 如果希望在子类中添加一些父类中没有的属性或方法直接加就行
* - 如果在子类中添加了和父类相同的方法,则子类方法会覆盖掉父类的方法
* 这种子类覆盖掉父类方法的形式,我们称为方法重写
*
*/
// 定义一个表示狗的类
// 使Dog类继承Animal类
class Dog extends Animal {
run() {
console.log(`${this.name}在跑~~~`);
}
sayHello() {
console.log('汪汪汪汪!');
}
}
// 定义一个表示猫的类
// 使Cat类继承Animal类
class Cat extends Animal {
sayHello() {
console.log('喵喵喵喵!');
}
}
const dog = new Dog('旺财', 5);
const cat = new Cat('咪咪', 3);
console.log(dog);
dog.sayHello();
dog.run();
console.log(cat);
cat.sayHello();
})();
(function () {
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello() {
console.log('动物在叫~');
}
}
class Dog extends Animal {
age: number;
// 如果在子类中写了构造函数,在子类构造函数中"必须"对父类的构造函数进行调用
constructor(name: string, age: number) {
// 调用父类的构造函数
super(name);
this.age = age;
}
sayHello() {
// 在类的方法中 super就表示当前类的父类
//super.sayHello();
console.log('汪汪汪汪!');
}
}
const dog = new Dog('旺财', 3);
dog.sayHello();
})();
(function () {
/*
* 以abstract开头的类是抽象类,
* 抽象类和其他类区别不大,只是不能用来创建对象
* 抽象类就是专门用来被继承的类
*
* 抽象类中可以添加抽象方法
*/
abstract class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
// 定义一个抽象方法
// 抽象方法使用 abstract开头,没有方法体
// 抽象方法只能定义在抽象类中,子类必须对抽象方法进行重写
abstract sayHello(): void;
}
// 下面的代码会报错
// const an = new Animal();
class Dog extends Animal {
sayHello() {
console.log('汪汪汪汪!');
}
}
class Cat extends Animal {
sayHello() {
console.log('喵喵喵喵!');
}
}
const dog = new Dog('旺财');
dog.sayHello();
})();
(function () {
// 定义一个表示人的类
class Person {
// TS可以在属性前添加属性的修饰符
/*
* public 修饰的属性可以在任意位置访问(修改)默认值
* private 私有属性,私有属性只能在类内部进行访问(修改)
* - 通过在类中添加方法使得私有属性可以被外部访问
* protected 受保护的属性,只能在当前类和当前类的子类中访问(修改)
*
*/
private _name: string;
private _age: number;
constructor(name: string, age: number) {
this._name = name;
this._age = age;
}
/*
* getter方法用来读取属性
* setter方法用来设置属性
* - 它们被称为属性的存取器
*/
// 定义方法,用来获取name属性
// getName(){
// return this._name;
// }
// 定义方法,用来设置name属性
// setName(value: string){
// this._name = value;
// }
// getAge(){
// return this._age;
// }
// setAge(value: number){
// // 判断年龄是否合法
// if(value >= 0){
// this._age = value;
// }
// }
// TS中设置getter方法的方式
// 此时再使用per.name时,实际上是调用了get name()方法!
get name() {
console.log('get name()执行了!!');
return this._name;
}
// TS中设置setter方法的方式
// 此时再使用per.name = xxx时,实际上是调用了set name()方法!
set name(value) {
console.log('set name()执行了!!');
this._name = value;
}
get age() {
return this._age;
}
set age(value) {
if (value >= 0) {
this._age = value
}
}
}
const per = new Person('孙悟空', 18);
/*
* 现在属性是在对象中设置的,属性可以任意的被修改,
* 属性可以任意被修改将会导致对象中的数据变得非常不安全
*/
// per.setName('猪八戒');
// per.setAge(-33);
per.name = '猪八戒';
per.age = -33;
console.log(per);
/*
* protected演示
*/
class A {
protected num: number;
constructor(num: number) {
this.num = num;
}
}
class B extends A {
test() {
console.log(this.num);
}
}
const b = new B(123);
// b.num = 33;
/*
* 可以直接将属性定义在构造函数中
*
* 下面两个构造方法效果是一样的!
*/
/*
class C{
name: string;
age: number
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}*/
class C {
// 可以直接将属性定义在构造函数中
constructor(public name: string, public age: number) {
}
}
const c = new C('xxx', 111);
console.log(c);
})();
## 接口(Interface)
接口的作用类似于抽象类,不同点在于:接口中的所有方法和属性都是没有实值的,换句话说接口中的所有方法都是抽象方法;
接口主要负责定义一个类的结构,接口可以去限制一个对象的接口:对象只有包含接口中定义的所有属性和方法时才能匹配接口;
同时,可以让一个类去实现接口,实现接口时类中要保护接口中的所有属性;
示例(检查对象类型):
```typescript
interface Person{
name: string;
sayHello():void;
}
function fn(per: Person){
per.sayHello();
}
fn({name:'孙悟空', sayHello() {console.log(`Hello, 我是 ${this.name}`)}});
示例(实现):
interface Person{
name: string;
sayHello():void;
}
class Student implements Person{
constructor(public name: string) {
}
sayHello() {
console.log('大家好,我是'+this.name);
}
}
(function () {
// 描述一个对象的类型
type myType = {
name: string,
age: number
};
/*
* 接口用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法
* 同时接口也可以当成类型声明去使用
*/
interface myInterface {
name: string;
age: number;
}
interface myInterface {
gender: string;
}
// const obj: myInterface = {
// name: 'sss',
// age: 111,
// gender: '男'
// };
/*
* 接口可以在定义类的时候去限制类的结构,
* 接口中的所有的属性都不能有实际的值
* 接口只定义对象的结构,而不考虑实际值
* 在接口中所有的方法都是抽象方法
*/
interface myInter {
name: string;
sayHello(): void;
}
/*
* 定义类时,可以使类去实现一个接口,
* 实现接口就是使类满足接口的要求
*/
class MyClass implements myInter {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello() {
console.log('大家好~~');
}
}
})();
## 泛型(Generic)
定义一个函数或类时,有些情况下无法确定其中要使用的具体类型(返回值、参数、属性的类型不能确定);
此时泛型便能够发挥作用;
举个例子:
```typescript
function test(arg: any): any{
return arg;
}
上例中,test函数有一个参数类型不确定,但是能确定的时其返回值的类型和参数的类型是相同的;
由于类型不确定所以参数和返回值均使用了any,但是很明显这样做是不合适的:
首先使用any会关闭TS的类型检查,其次这样设置也不能体现出参数和返回值是相同的类型;
泛型函数
创建泛型函数
function test<T>(arg: T): T{
return arg;
}
这里的<T>
就是泛型;
T是我们给这个类型起的名字(不一定非叫T),设置泛型后即可在函数中使用T来表示该类型;
所以泛型其实很好理解,就表示某个类型;
那么如何使用上边的函数呢?
使用泛型函数
方式一(直接使用):
test(10)
使用时可以直接传递参数使用,类型会由TS自动推断出来,但有时编译器无法自动推断时还需要使用下面的方式
方式二(指定类型):
test<number>(10)
也可以在函数后手动指定泛型;
函数中声明多个泛型
可以同时指定多个泛型,泛型间使用逗号隔开:
function test<T, K>(a: T, b: K): K{
return b;
}
test<number, string>(10, "hello");
使用泛型时,完全可以将泛型当成是一个普通的类去使用;
泛型类
类中同样可以使用泛型:
class MyClass<T>{
prop: T;
constructor(prop: T){
this.prop = prop;
}
}
泛型继承
除此之外,也可以对泛型的范围进行约束
interface MyInter{
length: number;
}
function test<T extends MyInter>(arg: T): number{
return arg.length;
}
使用T extends MyInter表示泛型T必须是MyInter的子类,不一定非要使用接口类和抽象类同样适用;
/*
function fn(a: any): any{
return a;
}
*/
/*
* 在定义函数或是类时,如果遇到类型不明确就可以使用泛型
*/
function fn<T>(a: T): T {
return a;
}
/* I.可以直接调用具有泛型的函数 */
// 1.不指定泛型,TS可以自动对类型进行推断
let result = fn(10);
// 2.指定泛型
let result2 = fn<string>('hello');
/* II.泛型可以同时指定多个 */
function fn2<T, K>(a: T, b: K): T {
console.log(b);
return a;
}
fn2<number, string>(123, 'hello');
/* III.限制泛型范围 */
interface Inter {
length: number;
}
// T extends Inter 表示泛型T必须时Inter实现类(子类)
function fn3<T extends Inter>(a: T): number {
return a.length;
}
fn3({length: 10});
/* Ⅳ.类中使用泛型 */
class MyClass<T> {
name: T;
constructor(name: T) {
this.name = name;
}
}
const mc = new MyClass<string>('孙悟空');