这是我参与「第四届青训营 」笔记创作活动的第6天
一、ts 开发环境安装
用 nodejs 安装 ts 的开发环境: npm i -g typescript与npm i -g ts-node
编写 ts 文件后执行tsc即可编译 ts 文件为 js 文件。通过tsc -init可生成配置文件。
二、ts 中的类型声明
let 变量: 类型;
let 变量: 类型 = 值;
function fn(参数: 类型,参数: 类型): 返回类型{
...
}
若值类型与变量类型不相同,则会报错。 与 js 相比,ts 增加了为变量声明类型的功能,在开发中可以避免很多麻烦。
三、ts 中的类型
| 类型 | 例 | 描述 |
|---|---|---|
| number | 1,-1,22 | 数字 |
| string | 'hello',"hello",`hello` | 字符串 |
| boolean | true,false | 布尔值 |
| 字面量 | 本身 | 变量值就是限制类型 |
| any | * | 任意类型 |
| unknown | * | 类型安全的 any |
| void | 空值(undefined) | 无值 |
| never | 没有值 | 不为任何值 |
| object | {age:20} | 任意 js 对象 |
| array | [1,2,3] | 任意 js 数组 |
| tuple | [1,2] | 元素,定长数组 |
| enum | enum{A,B} | 枚举 |
//字面量类型声明
let a: 10;
a = 10; //正确,可赋值为10
a = 20; //报错,只能为10
let b: "male" | "female"; // b只能等于"male"或"female"
b = "male"; //正确
b = "female" //正确
b = "hello" //报错
let c: number | string; //c只能为number类型和string类型
let d: any; //关闭类型检查,不建议使用
//声明变量若不指定类型,则自动判断any
//any类型变量可赋值给任何变量
let e: unknown = "hi";
//unknown类型可以被赋予任何值,但不能赋值给任何变量
let f:string = "hello";
f = e as string; //类型断言,可以让unknown类型变量赋值给相应变量
function fun():never{ //永远不会返回结果
//一般用于抛出异常
throw new Error('出错');
}
let g: {name:string,age?:age};
//属性名后加冒号代表属性可选。
let h: {name:string,[propName:string]?:any};
//[propName:string]?:any表示任意类型属性,且属性名为string,值可选
let i:(a: number,b: string)=>boolean;
//表示i为一个函数,且形参一为number类型,形参二为string类型,返回值为boolean类型
enum Gender{
Male = 1,
Female = 2
}
let j :{name:string,gender:Gender};
j={
name:"张三",
gender:Gender.Male // 1
}
四、ts 的类
class Person {
readonly name: string = "张三"; //readonly只读属性
static age: number = 20; //static静态属性
static hello() {
//lei'fan
}
}
const p = new Person();
p.hello(); //调用静态方法可以直接 类.方法名
五、ts 的构造函数和 this
创建构造函数
class Dog {
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
const dog = new Dog("狗1", 2);
this 代表 Dog 的每个实例对象,对象是谁 this 就是谁。
六、继承
class Animal {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
call() {
console.log("call");
}
}
class Dog extends Animal {
run() {
console.log("狗跑");
}
call() {
console.log("狗叫");
}
}
class Cat extends Animal {
run() {
console.log("猫跑");
}
call() {
console.log("猫叫");
}
}
继承将多个类中共有的代码写道一个父类中,供其他子类继承。
七、super 关键字
super 用于在子类中调用父类的方法或属性。
class Cat extends Animal {
run() {
console.log("猫跑");
}
call() {
super.call(); //输出call,调用的是父类方法
}
}
在子类构造函数中默认会调用 super(),并传入值。
class Cat extends Animal {
constructor(name: string, age: number) {
super(name, age);
}
run() {
console.log("猫跑");
}
call() {
super.call(); //输出call,调用的是父类方法
}
}
八、抽象类
抽象类以 abstract 开头,不能用于创建对象,只能用于继承。
abstract class Animal {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
abstract call(); //抽象方法,没有方法体,只能定义在抽象类中,子类必须进行重写。
}
九、接口
interface MyCode{ //定义一个类中应该包含哪些属性和方法
name: string;
age: number;
hello():string;
}
interface MyCode{ //可重复定义
hobby: string;
}
const obj: MyCode={ //将同名接口中的属性与方法都要定义
name:'张三';
age:20;
hobby:'打球';
}
interface MyCode {
//定义一个类中应该包含哪些属性和方法
name: string;
hello(): string;
}
//实现接口
class MyClass implements MyCode {
name: string;
constructor(name: string) {
this.name = name;
}
hello(): string {
return "hello";
}
}
-
接口可重复定义,但实际效果是将所有同名接口合为一个接口。
-
接口可以限制类的结构
-
接口中所有的属性没有实际的值
-
接口只定义对象的解构卖不考录实际值
十、属性的封装
class Cat {
private _name: string;
private _age: number;
constructor(name: string, age: number) {
this._name = name;
this._age = age;
}
get name(): string {
return this._name;
}
set name(value: string) {
this._name = value;
}
get age(): number {
return this._age;
}
set age(value: number) {
this._age = value;
}
}
- publich 可在任意位置访问(默认值)
- private 私有属性,私有属性只能在类内部访问
- protected 受保护的属性,只能在当前类以及其子类中进行访问
十一、泛型
在定义函数或类时,如果遇到类型不明确就可以使用泛型
function fn<T>(a: T): T {
return a;
}
fn(10); //不指定泛型
fn<string>("hello"); //指定泛型
function fn2<T, K>(a: T, b: K): T {
return a;
}
fn2(123, "123");
fn2<string>(123, "123");
interface Inter{
length: number;
}
function fn3<T extend Inter>(a:T):number{ //T必须是Inter的子类
return a.length;
}
fn3('123');
十二、联合/交叉类型
- 联合类型: IA | IB;联合类型表示一个值可以是几种类型之一。
- 交叉类型:IA & IB ; 多种类型叠加到一起成为一种类型,他包含了所需的所有类型的特性。
type IBookList = Array<{
author: string;
} & ({
type: 'history';
range: string;
} | {
type: 'story';
theme: string;
})>
类型保护与类型守卫
interface IA {a: 1, a1: 2}
interface IB {b: 1, b1: 2}
function log(arg: IA | IB){
// 程序会报错
// 访问联合类型时,处于程序安全,仅能访问联合类型中的交集部分
if(arg.a){
console.log(arg.a1)
}else{
console.log(arg.b1)
}
}
由于IB中不存在a属性,进行判断时,arg不一定是IA,故arg为IB时程序会产生异常。
修改后:
function getIsIA(arg: IA | IB): arg is IA{
// 类型守卫:定义一个函数,返回值为一个类型谓词,生效范围为子作用域
return !!(arg as IA).a
}
function log2(arg: IA | IB){
if(getIsIA(arg)){
console.log(arg.a1)
}else{
console.log(arg.b1)
}
}
通过getIsIA判断是否为IA,是则输出arg.a1,否则arg为IB,输出arg.b1。