TypeScript 学习
- TypeScript 和 JavaScript 一样是一门静态类型弱类型语言
- TypeScript 和 JavaScript 可以共存
- JavaScript 的类型分为两种:原始数据类型(Primitive data types)和对象类型(Object types)。
- 原始数据类型包括 boolean、number、string、null、undefined 以及 ES6 中的新类型 Symbol 和 ES10 中的新类型 BigInt。
- TypeScript tsconfig.js 查看详情
TypeScript 的静态类型
boolean
number
string
object
array
null
undefined
void
symbol
unknown (更安全的任意类型)
any (任意类型)
never (没有值, 通常用于无限循环的函数)
tuple (元组, TS 新增类型,固定常数数组)
enum (枚举, TS 新增类型 )
字面量 (let a: 10; let text: 'hello' | 'word')
在 TypeScript 中,boolean 是 JavaScript 中的基本类型,而 Boolean 是 JavaScript 中的构造函数。其他基本类型(除了 null 和 undefined)一样,不再赘述。
对象类型
// 例:
const obj: {
name: string;
age: number;
} = {
name: "张三",
age: 21,
};
数组类型
// 例:
const arr: string[] = ["张三", "李四", "王五"];
数组多类型
// 数组多类型
const arr1: (number | string)[] = [1, "string", 2];
数组对象类型定义
// 数组对象类型定义
const arr2: { name: string; age: number } = {
name: "张三",
age: 21,
};
类类型
// 例:
class MyClass {}
const newClass: MyClass = new MyClass();
类类型定义数组/对象
class MyClass2 {
constructor() {
this.name = "";
this.age = 0;
}
name: string;
age: number;
}
const obj2: MyClass2 = new MyClass2();
obj2.name = "张三";
obj2.age = 21;
const arr4: MyClass2[] = [
{
name: "张三",
age: 21,
},
{
name: "李四",
age: 22,
},
];
函数静态类型
// 函数类型
const myFun: () => string = (name: string = "") => {
return name;
};
元组类型
// 元组的基本应用
const arr5: (string | number)[] = ["str1", "str2", 1];
// 普通的数组注解无法限制类型的位置
const arr6: (string | number)[] = ["str1", 1, "str2", 2];
// 元组注解可以限制位置
const arr7: [string, string, number] = ["str1", "str2", 1];
// const arr7: [string, string, number] = ['str1', 1, 'str2'] // 报错
const arr8: [string, string, number][] = [
["str1", "str2", 1],
["张三", "李四", 2],
];
枚举类型
enum State {
STATE1,
STATE2,
STATE3
}
enum State2 {
STATE1 = 5,
STATE2,
STATE3
}
const enum Week {
Sunday = '周日',
Monday = '周一',
Tuesday = '周二',
Wednesday = '周三',
Thursday = '周四',
Friday = '周五',
Saturday = '周六'
}
console.log(State.STATE3); // 2
console.log(State2.STATE3); // 7
console.log(Week.Wednesday); // 周三
泛型 <>
泛型可以直接定义 type 类型。 泛型可以在书写方法时不定义类型,可以在方法使用时定义类型, 泛型可以同时指定多个。 泛型也可以使用 extends 进行约束。
const arrTest1: Array<string> = ['张三', '李四']
function addTest<T extends number | string, P>(_par1: T, _par2: P): T {
console.log(typeof _par2)
console.log(typeof _par2)
return _par1
}
addTest<string, string>('张三', '李四')
addTest(1, 2) // 泛型同样可以尽心类型推断
// addTest(true, false) // true 报错,因为约束泛型 T 只可以为 number 或者 string
interface people {
name: string;
}
class SelectPeople<T extends people> {
constructor(private peoples: T[]) { }
getPeoples(index: number): string {
return this.peoples[index].name;
}
}
const selectPeople = new SelectPeople([
{ name: "张三" },
{ name: "李四" },
{ name: "王五" },
]);
console.log(selectPeople.getPeoples(1)); // 张三
TypeScript 的 类型注解 和 类型推断
类型注解:注明变量的类型 类型推断:通过
TS自行推断变量类型 如果TS能够自动分析变量类型, 我们就什么也不需要做了 如果TS无法分析变量类型的话, 我们就需要使用类型注解
TypeScript 联合类型 |
const testSex1: (string | number) = ''
interface testType1 {
name: string
age: number
}
interface testType2 {
sex: (number | string)
}
const testJoint1: (testType1 | testType2) = {
name: '张三',
sex: '男'
}
TypeScript 类型断言 as
const snake: HTMLElement = document.getElementById('snake') as HTMLElement
类型检测 in 、 typeof 、 instanceof
类型检测 in 检测变量
interface inter1 {
fun1(): string
}
interface inter2 {
fun2(): string
}
const fun3Obj: (inter1 | inter2) = {
fun2() {
return 'Hello Word!'
}
}
function fun3(_obj: (inter1 | inter2)) {
if ('fun1' in _obj) {
_obj.fun1()
}
if ('fun2' in _obj) {
_obj.fun2()
}
}
类型检测 typeof 检测变量类型
function multiplicationFun(_num: any): number {
if (typeof _num === 'number') {
return _num * 2
} else {
return 0
}
}
类型检测 instanceof 。
instanceof 只能用在类上
class numberClass {
content = 0
}
function addNumClass(_obj1: object | numberClass, _obj2: object | numberClass) {
if (_obj1 instanceof numberClass && _obj2 instanceof numberClass) {
return _obj1.content + _obj2.content
}
}
TypeScript 的函数类型
在 TS 中,规定函数的类型可以使用静态类型方式定义
例:
// 函数类型
const myFun: () => string = (name: string = "") => {
return name;
};
但是这种定义方式太过复杂,我们可以通过下面这种方式进行定义参数类型和返回值类型
function mySum(num1: number, num2: number): number {
return num1 + num2;
}
const sum: number = mySum(1, 2);
函数参数为对象
错误的方式
// 错误:
function mySum2({ num1: number, num2: number }): number {
return num1 + num2;
}
正确的方式
function mySum2({ num1, num2 }: { num1: number; num2: number }): number {
return num1 + num2;
}
函数无返回值
function sayHello(): void {
console.log("hello world");
}
函数 never 返回值类型
如果一个函数是永远也执行不完的,就可以定义返回值为
never
function errorFun(): never {
throw new Error();
console.log("Hello World");
}
function forNever(): never {
while (true) {}
console.log("Hello World");
}
TypeScript 的类型别名
例:
type StrNum = {
name: string;
age: number;
};
const obj1: StrNum = {
name: "张三",
age: 21,
};
const arr3: StrNum[] = [
{ name: "张三", age: 21 },
{ name: "李四", age: 22 },
];
TypeScript 的 interface 接口
例:
/**
* @description 通过年龄和性别筛选
* @param {string} name
* @param {number} age
* @param {string} sex
* @example 筛选年纪小于 25 的女性
*/
function screeningFun(name: string, age: number, sex: string) {
age < 25 && sex === "女" && console.log(`姓名:${name} 晋升`);
age >= 25 && sex !== "女" && console.log(`姓名:${name} 淘汰`);
}
screeningFun("张三", 21, "女");
/**
* @description 展示晋升人员信息
*
* @param {string} name
* @param {number} age
* @param {string} sex
*/
function getScreeningFun(name: string, age: number, sex: string) {
age < 25 && sex === "女" && console.log(`姓名:${name} 年龄:${age}`);
}
getScreeningFun("张三", 21, "女");
通过以上可以发现同一种类型反复定义,此时就可以借助 interface 接口
interface inInformation {
name: string;
age: number;
sex: string;
}
function screeningFun2(obj: inInformation) {
obj.age < 25 && obj.sex === "女" && console.log(`姓名:${obj.name} 晋升`);
obj.age >= 25 && obj.sex !== "女" && console.log(`姓名:${obj.name} 淘汰`);
}
function getScreeningFun2(obj: inInformation) {
obj.age < 25 &&
obj.sex === "女" &&
console.log(`姓名:${obj.name} 年龄:${obj.age}`);
}
const information1: inInformation = {
name: "张三",
age: 21,
sex: "女",
};
screeningFun2(information1);
getScreeningFun2(information1);
interface 接口和 type 类型别名定义非必选的值
例:
type typeInformation = {
name: string;
age: number;
sex: string;
site?: string;
};
interface inInformation2 {
name: string;
age: number;
sex: string;
site?: string;
}
function screeningFun3(obj: typeInformation) {
console.log(`姓名:${obj.name} 年龄:${obj.age}`);
obj.site && console.log(`地址:${obj.site}`);
}
function screeningFun4(obj: inInformation2) {
console.log(`姓名:${obj.name} 年龄:${obj.age}`);
obj.site && console.log(`地址:${obj.site}`);
}
const information2: inInformation2 = {
name: "张三",
age: 21,
sex: "女",
};
screeningFun3(information2);
screeningFun4(information2);
interface 接口和 type 类型别名可以允许加入任意值
例:
interface inInformation3 {
name: string;
age: number;
sex: string;
site?: string;
[propName: string]: any;
}
type typeInformation2 = {
name: string;
age: number;
sex: string;
site?: string;
[propName: string]: any;
};
const information3: inInformation3 = {
name: "张三",
age: 21,
sex: "女",
hobby: "看电影",
};
function screeningFun5(obj: inInformation3) {
console.log(`姓名:${obj.name} 年龄:${obj.age}`);
obj.site && console.log(`地址:${obj.site}`);
}
function screeningFun6(obj: typeInformation2) {
console.log(`姓名:${obj.name} 年龄:${obj.age}`);
obj.site && console.log(`地址:${obj.site}`);
}
screeningFun5(information3);
screeningFun6(information3);
interface 接口和 type 类型别名可以允许加入方法
例:
interface testInterfaceFun {
sum(num1: number, num2: number): number;
}
type testTypeFun = {
outputLog(): string;
};
const testInterfaceVariable: testInterfaceFun = {
sum(num1: number, num2: number) {
return num1 + num2;
},
};
const testTypeFunVariable: testTypeFun = {
outputLog() {
return "Hello Word!";
},
};
interface 接口和 type 类型别名的区别
- 类型别名可以直接给类型,比如 string ,而接口必须代表对象。
type str = string;
// interface str2 = string // 报错
- 接口可以继承接口或者类型别名,关键字:
extends,但是接口类型不可以继承
type fatherType = {
name: string
}
interface childInterface1 extends fatherType {
age: number
}
const extendsInterface1: childInterface1 = {
name: '张三',
age: 21
}
interface fatherInterface = {
name: string
}
interface childInterface2 extends fatherInterface {
age: number
}
const extendsInterface2: childInterface2 = {
name: '张三',
age: 21
}
// type a = {
// name: string
// }
// type b extends a = {
// age: number
// }
- 类型别名不可以重名,但是接口可以重名,但是里面相同的属性或方法类型必须相同
type repeatType = {
name: string;
age: number;
};
// 报错
// type repeatType = {
// sex: string
// }
interface repeatInterface {
name: string;
age: number;
sex: string;
}
interface repeatInterface {
// sex: number, // 报错
address: string;
}
const repeatTest: repeatInterface = {
name: "张三",
age: 21,
sex: "男",
address: "地球🌏",
};
TypeScript 的 class 类的使用
TS 面向对象
class 类名 {
属性: 类型
constructor (变量: 类型) {
this.属性 = 变量
}
方法(){}
}
约束: interface 和 type 对类的约束,关键字: implements 。
例:
interface testClassInterface {
num1: number;
num2: number;
sum(num1: number, num2: number): number;
}
class testInterface implements testClassInterface {
constructor(num1: number, num2: number) {
this.num1 = num1;
this.num2 = num2;
}
num1: number;
num2: number;
sum(num1 = this.num1, num2 = this.num2) {
return num1 + num2;
}
}
const myClass3 = new testInterface(1, 2);
console.log(myClass3.sum());
type testClassType = {
text: string;
outputLog(): string;
};
class testInterface2 implements testClassType {
constructor() {
this.text = "Hello Word";
}
text: string;
outputLog() {
return "Hello Word!";
}
}
类的继承,关键字: extends 。
class fatherClass {
public text = "father";
fatherOutputLog() {
console.log("father");
}
}
class childClass extends fatherClass {
childOutputLog() {
console.log("child");
}
outputThis() {
console.log(this);
}
}
const extendsClass = new childClass();
console.log(extendsClass.text); // father
extendsClass.fatherOutputLog(); // father
extendsClass.childOutputLog(); // child
extendsClass.outputThis(); // childClass { text: 'father' }
类的重写
class childClass2 extends fatherClass {
text = "child";
fatherOutputLog() {
console.log("child");
}
}
const extendsClass2 = new childClass2();
console.log(extendsClass2.text); // child
extendsClass2.fatherOutputLog(); // child
继承类调用被继承类的方法(可以在子类中调用父类的方法但无法获取属性),关键字: super 。
class childClass3 extends fatherClass {
constructor() {
super(); //继承时必需要在构造函数内调用 super() 方法
}
// text: string = super.text // 此处报错 super 无法获取属性
childOutputLog() {
console.log("child");
super.fatherOutputLog();
}
}
const extendsClass3 = new childClass3();
console.log(extendsClass3.text); // undefined
extendsClass3.childOutputLog(); // child father
类的访问类型
- public(允许在类的内部和外部被调用)
- private(只允许在类的内部被调用,外部不允许调用)
- protected(允许在类内及继承的子类中使用)
- static(静态类型,不需要 new 这个对象,就可以使用了,但是 static 不可以修饰参数)
- readonly(只读属性)
public 类型
class testClass1 {
public text: string = "Hello Word!";
public outputLog() {
console.log(this.text); // Hello Word!
}
}
//------ 以下为类的外部 ------//
const newTestClass1 = new testClass1();
newTestClass1.text = "你好!";
console.log(newTestClass1.text); // 你好!
newTestClass1.outputLog(); // 你好!
private 类型
class testClass2 {
private text: string = "Hello Word!";
private outputLog() {
console.log(this.text); // Hello Word!
}
}
//------ 以下为类的继承类 ------//
class testChildClass1 extends testClass2 {
outputChildLog() {
// super.outputLog() // 此处报错
}
}
//------ 以下为类的外部 ------//
const newTestClass2 = new testClass2();
// newTestClass2.text = '你好!' // 此处报错
// console.log(newTestClass2.text) // 此处报错
// newTestClass2.outputLog() // 此处报错
protected 类型
class testClass3 {
protected text: string = "Hello Word!";
protected outputLog() {
console.log(this.text); // Hello Word!
}
}
//------ 以下为类的继承类 ------//
class testChildClass2 extends testClass3 {
outputChildLog() {
super.outputLog(); // Hello Word!
}
}
//------ 以下为类的外部 ------//
const newTestClass3 = new testClass3();
// newTestClass3.text = '你好!' // 此处报错
// console.log(newTestClass3.text) // 此处报错
// newTestClass3.outputLog() // 此处报错
const newTestClass4 = new testChildClass2();
newTestClass4.outputChildLog(); // Hello Word!
static 类型
使用 static 修饰的函数,不需要 new 这个对象,就可以使用了,但是 static 不可以修饰参数
class testClass12 {
static age: number
staticoutputLog(name: string) {
console.log(`姓名:${name} 年龄:${this.age}`)
}
// constructor(static _text: string) { } // 此处报错
}
testClass12.outputLog('李四') // Hello Word!
testClass12.age = 21
testClass12.outputLog('张三') // Hello Word!
readonly 只读属性
readonly 定义的变量赋值后,不可以再更改
class testClass13 {
constructor(readonly text: string) { }
}
const newTestClass13 = new testClass13('张三')
console.log(newTestClass13.text)
// newTestClass13.text = '张三' // 此处报错
类的构造函数
构造函数就是在类被初始化的时候,自动执行的一个方法。
class testClass4 {
public text: string = "";
constructor(text: string) {
this.text = text;
}
outputLog() {
console.log(this.text);
}
}
const newTestClass5 = new testClass4("Hello Word!");
newTestClass5.outputLog(); // Hello Word!
// 可以简写
class testClass5 {
constructor(public text: string) {}
outputLog() {
console.log(this.text);
}
}
const newTestClass6 = new testClass5("Hello Word!");
newTestClass6.outputLog(); // Hello Word!
类继承中的构造器
class testClass6 {
constructor(public name: string) {}
}
class testChildClass3 extends testClass6 {
constructor(public age: number) {
super("张三"); // 子类继承父类必需要调用 super() 来调用父类的构造函数
}
}
const newTestClass7 = new testChildClass3(21);
console.log(newTestClass7.name); // 张三
console.log(newTestClass7.age); // 21
class testClass7 {}
class testChildClass4 extends testClass7 {
constructor(public text: string) {
super(); // 即使父类没有定义构造函数,但是如果子类定义构造函数,子类则必须调用 super() ,因为父类此时默认有一个构造函数 constructor() { }
}
}
类的 Getter 和 Setter 。
Getter
private 访问类型仅限类内部调用和修改,但是如果想让外部获取到此变量,则需要使用 getter 属性 getter 是一个属性, getter 属性的关键字是 get ,后面跟着类似方法的东西,但它并不是方法,它只是一个属性 类似于 vueX 里面的 getter
class testClass8 {
constructor(private _text: string) { }
get text() {
return this._text
}
}
const newTestClass8 = new testClass8('Hello Word!')
// newTestClass8.text = '你好!' // 报错
console.log(newTestClass8.text); // Hello Word!
// 通过 get 属性我们可以获取类的私有属性,虽说多此一举,但是能够保护类的属性不被破坏,并且可以自定义返回属性
class testClass9 {
constructor(private _text: string) { }
get text() {
return this._text + ' 你好,世界!'
}
}
const newTestClass9 = new testClass9('Hello Word!')
console.log(newTestClass9.text); // Hello Word! 你好,世界!
Setter
private 访问类型仅限类内部调用和修改,但是如果想让外部获修改此变量,则需要使用 setter 属性 setter 是一个属性, setter 属性的关键字是 set ,后面跟着类似方法的东西,但它并不是方法,它也只是一个属性 类似于 vueX 里面的 mutation
class testClass10 {
constructor(private _text: string) { }
get text() {
return this._text + ' 你好,世界!'
}
set text(text: string) {
this._text = text
}
}
const newTestClass10 = new testClass10('Hello Word!')
console.log(newTestClass10.text); // Hello Word! 你好,世界!
newTestClass10.text = '张三'
console.log(newTestClass10.text); // 张三 你好,世界!
// 通过 set 属性我们可以更改类的私有属性,虽然违背了私有属性的含义,但是我们可以自定义设置的数值
class testClass11 {
constructor(private _text: string) { }
get text() {
return this._text + ' 你好,世界!'
}
set text(text: string) {
this._text = '你好,' + text + '!'
}
}
const newTestClass11 = new testClass11('Hello Word!')
console.log(newTestClass11.text); // Hello Word! 你好,世界!
newTestClass11.text = '张三'
console.log(newTestClass11.text); // 你好,张三! 你好,世界!
抽象类 abstract 。
抽象类的关键词是 abstract ,里面的抽象方法/变量也是 abstract 一个类继承一个抽象类,则这个类必须实现所继承的抽象类里面定义的所有的抽象方法和抽象变量
abstract class abstractClass1 {
abstract name: string
abstract age: number
}
class abstractClass4 extends abstractClass1 {
name: string
age: number
constructor(name: string, age: number) {
super()
this.name = name
this.age = age
}
}
抽象类可以继承抽象类,组成一个抽象类
interface interfaceAb {
name: string
age: number
sex: string
}
abstract class abstractClass2 extends abstractClass1 {
abstract sex: string
abstract outputLog(obj: interfaceAb): void
}
abstract class abstractClass3 extends abstractClass2 {
abstract multiplication(num1: number, num2: number): number
}
class abstractClass5 extends abstractClass3 {
name: string
age: number
sex: string
constructor(obj: interfaceAb) {
super()
this.name = obj.name
this.age = obj.age
this.sex = obj.sex
}
multiplication(num1: number, num2: number): number {
return num1 * num2
}
outputLog(obj: interfaceAb): void {
console.log(`姓名:${obj.name} 性别:${obj.sex} 年龄:${obj.sex}`)
}
}
命名空间 namespace 。
命名空间支持嵌套
namespace Home {
class Class1 {
}
class Class2 {
}
export class Class3 {
}
}
// console.log(Home.Class1); // 报错不存在
console.log(Home.Class3); // 爆露出来的 class 可以获取