# typescript巩固
# 变量类型声明
# 基本类型声明
let u: undeinfed = undefined;
let n: null = null;
let num: number = undefined;
let notSure: any = 4;
notSure = "maybe it is a string";
notSure = true;
// 联合声明
let numberOrString: number | string = 234;
# 数组、元组类型声明
// 数组元素只能是number类型
let arrOfNumbers: number[] = [1, 2, 3, 4];
arrOfNumbers.push(5);
function test() {
console.log(arguments);
}
// 元组类型 规定了必须传入2个数组元素,且对应位置必须为对应类型。
let user: [string, number] = ["yunshangzhou", 1];
# 接口 Interface 声明
/**
* 定义一个对象里有什么样的属性,属性的类型是什么。
* 在对一个对象声明该接口时,属性不能多,也不能少。数量固定。
* 如果接口定义的属性名,后面加了问号 ?,则代表可选择,声明接口时,属性可忽略。
* */
interface Person {
name: string;
age?: number; // 该属性可在实例中不定义
}
let cloud: Person = {
name: "cloud",
age: 20,
};
# function 声明
// 注意,必选参数不可在可选参数之后,否则报错
// 可声明函数返回值为什么类型
function add(x: number, y: number, z?: number): number {
if (typeof z === "number") {
return x + y + z;
} else {
return x + y;
}
}
let result = add(2, 3);
// 声明一个和add一样类型的函数
// 在赋值前头声明函数,返回值类型则用 => 箭头来定义
const add2: (x: number, y: number, z?: number) => number = add;
# 类型推测
当创建一个变量而没有声明类型时,ts 将会自动推测该变量的类型
let str = "str";
str = 123; // 报错
# 类 Class
类(Class) : 定义了一切事物的抽象特点 对象(Object) : 类的实例 面向对象(OOP) 三大特性: 封装、继承、多态
# 什么是封装?
将数据的操作细节隐藏起来,只暴露对外的接口。调用端不需要知道细节,只需要知道如何使用即可。
# 什么是继承?
子类继承父类,子类拥有父类的一些特性
# 什么是多态?
多态是由继承产生的相关的类,这些类子类重写了父类的方法。因为继承的关系,他们的类型和父类是一样的,因此使用子类时,同名函数执行出来的结果就为多个形态。
// 父类
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
run() {
return `${this.name} is run ning`;
}
}
// 子类继承
class Dog extends Animal {
// 子类内部的新方法
bark() {
return `${this.name} is barking`;
}
}
// 子类继承
class Cat extends Animal {
constructor(name: string) {
super(name);
console.log(this.name);
}
// run方法重写(Overwrite)
run() {
return "Meow, " + super.run();
}
}
// 继承 多态 创建子类 Dog实例
// const xiaobao = new Dog('xiaobao');
// console.log(xiaobao.run());
// console.log(xiaobao.bark());
// 继承 多态 创建子类 Cat实例
const maomao = new Cat("maomao");
console.log(maomao.run());
// 封装 创建父类 Animal实例
// const snake = new Animal('lily')
// console.log(snake.run())
# 属性修饰符
typescript 中有4种属性修饰符,它们分别是 public、private、protect、readonly。用法如下:
# public 修饰符
// public的name 具有可访问、可修改权限
class Animal {
public name: string;
constructor(name: string) {
this.name = name;
}
run() {
return `${this.name} is run ning`;
}
}
const snake = new Animal("lily");
console.log(snake.name); // lily
snake.name = "lucy";
console.log(snake.name); // lucy
# private 修饰符
将上述的 public name
,修改成 private name
,那么全局的 name 都会有红色下划线提示。Property 'name' is private and only accessible in within class 'Animal'
(name 属性是私有的,且只能在 Animal 类中通行),因此能知道 private
拥有不可访问性和不可修改性。
# protect 修饰符
将上述的 public name
,修改成 protected name
。则子类可以享有 name 属性的访问权限及修改权限。(属性“name”受保护,只能在类“Animal”及其子类中访问)。
# static 修饰符
它可以表明哪些属性为静态属性。在类没有实例化的情况下,可以直接访问到这些类中的属性。
class Animal {
name: string;
static catagories: string[] = ["mammal", "bird"];
static isAnimal(a: Object) {
return a instanceof Animal;
}
constructor(name: string) {
this.name = name;
}
run() {
return `${this.name} is run ning`;
}
}
const snake = new Animal("lily");
console.log(Animal.catagories); // ['mammal','bird']
console.log(Animal.isAnimal(snake)); // true
# readonly 只读修饰符
顾名思义,除了读取外,无法做任何操作
class Person {
readonly name = "Alice";
}
let p = new Person();
console.log(p.name); // Alice
TIP
以上四种修饰符:访问修饰符、只读修饰符和静态修饰符可以组合修饰同一成员 但需要注意
修饰符是可选的,在没有写任何修饰符,默认有个 public 同类修饰符只能有一个 三种修饰符有先后顺序,分别是:访问、静态、只读
即:【public/static/protected】 【static 】【readonly】
# 接口与类
我们可以通过 extends、implements 重写接口或者类. 当继承或实现多个接口或类时,可以让接口继承其他接口。再将未继承的方法、属性放入集成接口即可。
interface Radio {
switchRadio(): void;
}
interface Battery {
checkBatteryStatus();
}
// 继承Radio中的方法,并将Battery方法写入
// interface RadioWithBattery extends Radio , Battery{
interface RadioWithBattery extends Radio {
checkBatteryStatus();
}
// 实现多个接口,用逗号隔开。
// class Cellphone implements Radio,Battery{
class Cellphone implements RadioWithBattery {
switchRadio() {}
checkBatteryStatus() {}
}
TIP
1、接口用于定义每个属性或函数是什么类型, 无须关心里面的具体值、具体函数实现。而类则是实现接口中每个属性和函数的具体内容。
2、implements 有个硬性要求,类必须按照接口中定义的一一写出,否则将会报错。
# 枚举 Enum
// enum.ts
enum Direction {
Up,
left,
right,
down,
}
console.log(Direction);
其打印出来的结果:
// {
// '0': 'Up',
// '1': 'left',
// '2': 'right',
// '3': 'down',
// Up: 0,
// left: 1,
// right: 2,
// down: 3
// }
其编译成 js 文件后的结果:
// enum.js
var Direction;
(function (Direction) {
Direction[(Direction["Up"] = 0)] = "Up";
Direction[(Direction["left"] = 1)] = "left";
Direction[(Direction["right"] = 2)] = "right";
Direction[(Direction["down"] = 3)] = "down";
})(Direction || (Direction = {}));
// 也可以把枚举看作是数组,取第0项
// 个人理解,更像是对象,下标与值互相映射。
console.log(Direction[0]); // Up
console.log(Direction.Up); // 0
// console.log(Direction);
可以看到 enum.js 做了一个 IIFE 自执行函数,利用默认值在 Direction 对象中,对属性做了双向映射处理。
TIP
若在 Direction 中,定义了一个属性为数字,那么紧跟其后且未赋值的属性,将会按数字+1 的顺序自动赋值。
# 泛型
泛型,个人理解它为一个不确定类型的 any。一开始为 any 类型,但是依靠某些变量,依靠 ts 自动推断后,ts 为他们声明了类型,此刻泛型就会从 any 转为这个类型。(可以拿 Promise 中的状态作比喻,一开始为 pending,根据具体逻辑判断是倾向 Fulfilled 还是 Rejected)
# 简单例子
创建一个函数,要求返回值的类型和输入值的类型相同,这里就用到了泛型,在函数后面加尖括号,内部写的声明什么字符按照开发者习惯,一般都以字母T为泛型声明。
function consoleNumber<T>(num: T): T {
return num;
}
// 光标移至res,显示 let res: number
let res = consoleNumber(3);
console.log(res); // 3
# 接口中使用泛型
// 这里的泛型T,U,在变量声明时必须定义参数类型.
interface KeyPair<T, U> {
key: T;
value: U;
}
// 定义了接口的参数类型 number,string
let kp1: KeyPair<number, string> = { key: 123, value: "str" };
# 在类中使用泛型
class Queue<T> {
private data: T[] = [];
push(item: T): void {
this.data.push(item);
}
// 在立即执行shift()时,将会返回undefined。这就是为什么要规定返回类型为T | undefined
pop(): T | undefined {
return this.data.shift();
}
}
// 可在实例后定义泛型的具体类型
const queue = new Queue();
// const queue2 = new Queue<string>(); // 传入内容只能为字符串类型
// const queue3 = new Queue<number>(); // 传入内容只能为数字类型
queue.push(1);
queue.push("str");
console.log(queue.pop()); // 1
console.log(queue.pop()); // str
# 在函数声明时使用泛型
function plus(a:number,b: number,c: number): number{
return a + b + c;
}
// 为plus函数定义配套的接口
interface plusInterface<T>{
(a:T,b:T,c:T):T;
}
// 声明时,定义number类型即可
let pp: plusInterface <number> = plus;
console.log(pp(1,2,3)); // 6
# 类型别名(type aliases)
可将ts类型声明当作一种变量来声明并使用。
// 声明一个返回值为string的function 的类型
type NameResolver = () => string;
// 在其他类型别名中使用它
type NameOrResolver = string | NameResolver;
// 与下面类型别名的效果是一样的
// type NameOrResolver = string | (() => string);
// 参数n为string | (()=>string)
function getName(n: NameOrResolver): string {
if(typeof n === 'string'){
return n
}else{
return n();
}
}
# 类型断言(type assertion)
当出现联合类型时,参数需要确定成某一类型,才能让代码正常执行下去。这时就需要类型断言。
/**
* type assertion 类型断言
* */
function getLength(input: string | number) : number{
// input 断言成了 String类型
// const str = input as String
// if (str.length) {
// return str.length
// } else {
// // input 断言成了 Number类型
// const number = input as Number
// return number.toString().length;
// }
// 以下代码段效果等同于上面注释部分
if((<string>input).length){
return (<string>input).length;
}else{
return (<number>input).toString().length;
}
}
WARNING
只能断言已声明的联合类型。否则将提示报错。
# 声明(declare)
declare可以声明文件,也可以声明变量,声明函数。
// 声明函数类型,参数类型
declare function func(str: string): void;
// 声明变量类型
declare var ant:string;
//声明常量并赋值1
declare const num:1;
//声明类
declare class Cat{
static name:string;
static getAge(): number;
getName(id: number): string;
}
//声明命名空间
declare namespace space{
function func(str: string): string;
let num: number;
}
// 引入模块,定义其中的属性
declare module "foo" {
export let a: number;
export function b(): number;
export namespace c{
let cd: string
}
}
# 在项目中声明文件
项目目录下,创建xxx.d.ts
文件,如jQuery.d.ts
文件。这样全局都能共享到文件里的声明。
declare var jQuery: (selector: string) => any
# 配置tsc
如果没有声明,则需要配置一下tsc。在项目目录下创建tsconfig.json
。设置完后,在项目中代码行中输入jQuery,就能弹出相应提示。
{
"include":["**/*"]
}
# 下载npm上的ts模块
npm上的ts模块,下载后,可直接在项目中使用。代码行输入后就有相应提示。比如@types/react
, @types/jquery
npm install --save @types/jquery