文章目录
初识TS
npm install -g typescript 下载ts到全局
tsc -v 查看版本
同级创建tsconfig.json 文件 用于ts的配置
cmd中执行tsc hello.ts 可以创建出 js
ts代码报错也可以生成js文件
const hello = (name: string) => {
return `hello${name}`;
};
hello("liusheng");
// hello(123); 会报错,编译也会报错
// tsc -w class.ts 监听变化,生成js文件
// npm install -g ts-node 一步运行
// ts-node class.ts
tsc --init 生成配置文件
数据类型
基础类型
- boolean
- number
- string
- null和undefined
/*
原始数据类型
*/
// let isDone: boolean = 123;
let isDone: boolean = false;
let age: number = 13;
let binaryNumber: number = 0b1111; //es6 二进制和八进制
let firstName: string = "haohao";
let message: string = `Hello,${firstName},age is ${age}`;
let u: undefined = undefined;
let n: null = null;
// let num: number = "string"; //报错
let num: number = undefined;
void类型
- void类型像是与any类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void:
function warnUser(): void {
console.log("This is my warning message");
}
warnUser();
// 注意:声明一个void类型的变量没有什么大用,因为你只能为它赋予undefined和null
let unusable: void = undefined;
never类型
- 这个类型和 void 有些相似,也是不返回值,标示函数,比如:
function error(): never { throw new Error("error"); } error(); // function endless(): never { // while (true) { // console.log(111); // } // } // endless();
- 通俗理解 void 与 never:
- void 可以理解为一个没有 return 但能执行完毕的函数;
never 是一个函数体无法结束执行的函数;
any类型
/*
第三方,无法确定类型,any:宽泛的约束条件
*/
let notSure: any = "4";
notSure = `maybe it is a string`;
notSure = true;
notSure.myName;
notSure.getName();
// any类型 在.调用方法时没有提示
firstName.charCodeAt; //string 有提示
notSure.charCodeAt; //any 没提示
联合类型
/*
联合类型 可以赋值几个类型
*/
let numberOrString: number | string = 5;
numberOrString = "55";
// numberOrString = true; //报错
数组
/*
数组
类型 + [] 表示
*/
// let arrOfNumber: number[] = [1, 2, "str", 4];//报错
let arrOfNumber: number[] = [1, 2, 3, 4];
arrOfNumber.push(5);
// arrOfNumber.push("str");//报错
类数组
/*
类数组
*/
function test() {
console.log(arguments); //IArguments
// arguments.length
// arguments[1]
// arguments.forEach//报错,类数组找不到数组原型的方法
// let arr: any[] = arguments;//报错
}
元组
/*
元组 tuple
*/
// 第一项 字符串 第二项 boolean 第三项 数字 用any就丧失了意义
// let user: [string, number] = ["string"];//数组里多参数,少参数也会报错
let user: [string, number] = ["string", 1];
// user = [1, "string"];//报错,类型不能给错,按顺序
枚举
// enums 枚举
// 数字枚举
// enum Direction {
// Up,
// Down,
// Left,
// Right,
// }
// console.log(Direction.Up); //0
// console.log(Direction[0]); //'Up'
/*
// tsc 编译后 -> js文件
var Direction;
(function (Direction) {
Direction[Direction["Up"] = 0] = "Up";
Direction[Direction["Down"] = 1] = "Down";
Direction[Direction["Left"] = 2] = "Left";
Direction[Direction["Right"] = 3] = "Right";
})(Direction || (Direction = {}));
console.log(Direction.Up); //0
console.log(Direction[0]); //'Up'
*/
// 字符串枚举
// enum Direction {
// Up = "UP",
// Down = "DOWN",
// Left = "LEFT",
// Right = "RIGHT",
// }
// const value = "UP";
// if (value === Direction.Up) {
// console.log("go up!");
// }
// 常量枚举 const enum
const enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
const value = "UP";
if (value === Direction.Up) {
console.log("go up!");
}
// tsc 编译后 -> js文件
// var value = "UP";
// if (value === "UP" /* Up */) {
// console.log("go up!");
// }
interface接口
/*
interface 接口 :规范 和 契约 规定了样子
对对象的形状进行描述
对类进行抽象
Duck Typing (鸭子类型)
描述 object 的样子
描述 类, 函数 ,数组 都可以用 interface 描述
*/
// 定义一个接口
// 完全匹配形状
interface Person {
//Person -> IPerson 有的名字前加I 告诉是 Interface
//这里面属性分隔就是分号分隔,不是逗号
name: string;
age: number;
}
// viking -> Person 类型 ,约束 viking 的 object 类型就是Person那样
let viking: Person = {
//对象里不能少个属性或者多个属性
name: "songJing",
age: 20,
};
// 不完全匹配形状 age?: number;
interface PersonLiu {
name: string;
age?: number;
}
let vikingLiu: PersonLiu = {
// age 可以写或者不写
name: "songJing",
};
// 创建的时候的只读属性 readonly
interface PersonShe {
readonly id: number; //用于属性上的 放在前面
name: string;
age?: number;
}
let vikingShe: PersonShe = {
id: 1245,
name: "songJing",
};
// vikingShe.id = 1111;//报错 只读属性,不能重新赋值
函数
// js中函数是一等公民
// 和其他类型平等 传参,存数组,被返回,赋值给变量 等
// function add(x: number, y: number, z: number = 10): number {
// // y: number, z?: number 必选参数不能位于可选参数后
// //(x: number, y: number): number 最后的 number 就是返回的类型
// // z: number = 10 es6 加默认值&&最后一个参数是可选的
// if (typeof z === "number") {
// return x + y + z;
// } else {
// return x + y;
// }
// }
// let result = add(2, 3);
// 函数表达式
const add = function add(x: number, y: number, z: number = 10): number {
// y: number, z?: number 必选参数不能位于可选参数后
//(x: number, y: number): number 最后的 number 就是返回的类型
// z: number = 10 es6 加默认值 最后一个参数是可选的
if (typeof z === "number") {
return x + y + z;
} else {
return x + y;
}
};
// :(x: number, y: number, z?: number) => number 冒号后面是ts中申明类型
// const add2: number = add; //报错
const add2: (x: number, y: number, z?: number) => number = add; //ts类型推测
// let str = "str";
// str = "ddd";
// str = 123;//报错,没有指定类型,ts会自己推测 -> 类型推论
类
类 :定义一切事物的抽象特点
对象:类的实例
面向对象 oop 封装,继承,多态
类中的修饰符
pulic 共有的
private 属性“name”为私有属性,只能在类“Animal”中访问,外部和子类也不可以访问
protected 子类可以访问属性,外部不可以访问
readonly 只读不能重新赋值
class Animal {
public name: string;
// private name: string;
// protected name: string;
// readonly name: string;
// 静态属性
static categoies: string[] = ["mammal", "bird"];
// 静态方法:当类中定义和实例的方法没有多大关系时
static idAnimal(a) {
return a instanceof Animal;
}
constructor(name: string) {
this.name = name;
}
run() {
return `${this.name} is running`;
}
}
console.log(Animal.categoies);
const snake = new Animal("lily");
console.log(Animal.idAnimal(snake));
// console.log(snake.run());
// console.log(snake.name);
snake.name = "lucy";
// console.log(snake.name);
class Dog extends Animal {
bark() {
return `${this.name} is bark`;
}
}
const maomao = new Dog("maomao");
// console.log(maomao.run());
// console.log(maomao.bark());
class Cat extends Animal {
constructor(name) {
super(name);
// console.log(this.name);
}
run() {
//super.run 调用父类的方法
return "Meow," + super.run();
}
}
const sanhua = new Cat("sanhua");
// console.log(sanhua.run());
类和接口
//两个类都用一个方法,用 interface 定义约束
// class Car {
// switchRadio() {}
// }
// class Cellphone {
// switchRadio() {}
// }
// interface 定义约束 implements 抽象类的属性和方法
// 抽象验证类的属性和方法
interface Radio {
// void 不返回
switchRadio(triggerL: boolean): void;
}
interface Battery {
checkBatteryStatus();
}
// 接口也可以继承
interface RadioWithBattery extends Radio {
checkBatteryStatus();
}
class Car implements RadioWithBattery {
switchRadio() {}
checkBatteryStatus() {}
}
// implements Radio, Battery 两个接口,用逗号隔开就行
class Cellphone implements Radio, Battery {
switchRadio() {}
checkBatteryStatus() {}
}
泛型
// BUG
// function echo(arg: any): any {
// return arg;
// }
// const result: string = echo(123);
// 在函数中使用泛型
//泛型出现的动机 ,在函数名称后面加 <> 里面可以写泛型的名称 T 类似占位符或者变量
function echo<T>(arg: T): T {
return arg;
}
// const str: string = "str";
// const result = echo(str);
// const result: string = echo(true);//报错,不会像之前一样不报错
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
const result2 = swap(["str", 12]);
// result2[1]. //调用字符串的方法
// 约束泛型
function echoWithArr<T>(arg: T[]): T[] {
console.log(arg.length);
return arg;
}
const arrs = echoWithArr([1, 2, 3]);
interface IWithLength {
length: number;
}
function echoWithLength<T extends IWithLength>(arg: T): T {
console.log(arg.length);
return arg;
}
const str = echoWithLength("str");
const obj = echoWithLength({ length: 10, width: 10 });
const arr2 = echoWithLength([1, 2, 3]);
// echoWithLength(13); //报错,类型“13”的参数不能赋给类型“IWithLength”的参数。
// 在类中使用泛型
class Queue<T> {
private data = [];
push(item: T) {
return this.data.push(item);
}
pop(): T {
return this.data.shift();
}
}
const queue = new Queue<number>();
queue.push(1);
console.log(queue.pop().toFixed());
const queue2 = new Queue<string>();
queue2.push("str");
console.log(queue2.pop().length);
// 在 interface 中使用泛型
interface KeyPair<T, U> {
key: T;
value: U;
}
let kp1: KeyPair<number, string> = { key: 123, value: "str" };
let kp2: KeyPair<string, number> = { key: "22", value: 123 };
let arr: number[] = [1, 2, 3];
let arrTwo: Array<number> = [1, 2, 3];
//
interface IPlus<T> {
(a: T, b: T): T;
}
function plus(a: number, b: number): number {
return a + b;
}
function connect(a: string, b: string): string {
return a + b;
}
const a: IPlus<number> = plus;
const b: IPlus<string> = connect;
命名空间
命名空间:
在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、类、接口等放置到命名空间内
同Java的包、.Net的命名空间一样,TypeScript的命名空间可以将代码包裹起来,只对外暴露需要在外部访问的对象。
命名空间和模块的区别:
命名空间:内部模块,主要用于组织代码,避免命名冲突。(不同命名空间,类名称一样也可以)
模块:ts的外部模块的简称,侧重代码的复用,一个模块里可能会有多个命名空间。
/*
命名空间:在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、类、接口等放置到命名空间内
*/
import { A, B } from "./animal";
var d = new A.Dog("小黑");
d.eat();
var dog = new B.Dog("毛毛");
dog.eat();
- animal.ts
export namespace A { interface Animal { name: string; eat(): void; } export class Dog implements Animal { name: string; constructor(theName: string) { this.name = theName; } eat() { console.log(`${this.name} 在吃狗粮。`); } } export class Cat implements Animal { name: string; constructor(theName: string) { this.name = theName; } eat() { console.log(`${this.name} 在吃猫粮。`); } } } export namespace B { interface Animal { name: string; eat(): void; } export class Dog implements Animal { name: string; constructor(theName: string) { this.name = theName; } eat() { console.log(`${this.name} 在吃狗粮。`); } } export class Cat implements Animal { name: string; constructor(theName: string) { this.name = theName; } eat() { console.log(`${this.name} 在吃猫粮。`); } } }
装饰器
通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上扩展类、属性、方法、参数的功能。
常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器
装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参)
装饰器是过去几年js最大的成就之一,以是Es7的标准特性之一
- 类装饰器:类装饰器在类声明之前被声明(紧靠着类声明)。类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。
- 装饰器-ES6
- 装饰器-TypeScript
装饰器的实验支持
- tsconfig.json
{ "compilerOptions": { "experimentalDecorators": true //装饰者的实验支持 } }
装饰器的写法
- 普通装饰器(无法传参)
function logClass(params: any) {
console.log(params);
// params 就是当前类
params.prototype.apiUrl = "动态扩展的属性";
params.prototype.run = function () {
console.log("我是一个run方法");
};
}
@logClass
class HttpClient {
constructor() {}
getData() {}
}
var http: any = new HttpClient();
console.log(http.apiUrl);
http.run();
- 装饰器工厂(可传参)
function logClass(params: string) {
return function (target: any) {
console.log(target);
console.log(params);
target.prototype.apiUrl = params;
};
}
// @logClass("hello")
@logClass("zhainanya")
class HttpClient {
constructor() {}
getData() {}
}
var http: any = new HttpClient();
console.log(http.apiUrl);
类装饰器
- 重载构造函数
- 类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。
- 如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明
function logClass(target: any) {
console.log(target);
return class extends target {
apiUrl: any = "我是修改后的数据";
getData() {
this.apiUrl += "----";
console.log(this.apiUrl);
}
};
}
@logClass
class HttpClient {
public apiUrl: string | undefined;
constructor() {
this.apiUrl = "我是构造函数里面的apiUrl";
}
getData() {
console.log(this.apiUrl);
}
}
var http = new HttpClient();
http.getData();
属性装饰器
- 属性装饰器表达式会在运行时当作函数被调用,传入下列两个参数:
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 成员的名字。
// 类装饰器
function logClass(params: string) {
return function (target: any) {
// console.log(target);
// console.log(params);
};
}
// 属性装饰器
function logProperty(params: any) {
return function (target: any, attr: any) {
console.log(target);
console.log(attr);
target[attr] = params;
};
}
@logClass("xxx")
class HttpClient {
@logProperty("xxx.com")
public url: any | undefined;
constructor() {}
getData() {
console.log(this.url);
}
}
var http: any = new HttpClient();
http.getData();
方法装饰器
它会被应用到方法的属性描述符上,可以用来监视,修改或者替换方法定义。
方法装饰会在运行时传入下列三个参数:
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
- 成员的名字
- 成员的属性描述符
- tsc class-decorator.ts --target ES5 -w --experimentalDecorators 运行
- 方法装饰器
function get(params: any) {
return function (target: any, methodName: any, desc: any) {
console.log(target);
console.log(methodName);
console.log(desc);
target.apiUrl = 123;
target.run = function () {
console.log("run");
};
};
}
class HttpClient {
public url: any | undefined;
constructor() {}
@get("xxx")
getData() {
console.log(this.url);
}
}
var http: any = new HttpClient();
console.log(http.apiUrl);
http.run();
- 方法装饰器
function get(params: any) {
return function (target: any, methodName: any, desc: any) {
console.log(target);
console.log(methodName);
console.log(desc, desc.value);
// 修改装饰器的方法,把装饰器方法里面传入的所有参数改为string类型
var oMthod = desc.value;
desc.value = function (...args: any[]) {
args = args.map((value) => {
return String(value);
});
console.log(args, oMthod);
oMthod.apply(this, args);
};
};
}
class HttpClient {
public url: any | undefined;
constructor() {}
@get("maomao")
getData(...args: any[]) {
console.log(args, "==");
console.log("getData=====");
}
}
var http: any = new HttpClient();
http.getData(123, "xxx");
方法参数装饰器
参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素数据,传入下列三个参数
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 方法的名称
- 参数在函数参数列表中的索引
function logParams(params: any) { return function (target: any, methodName: any, paramsIndex: any) { console.log(params); console.log(target); console.log(methodName); console.log(paramsIndex); target.apiUrl = params; }; } class HttpClient { public url: any | undefined; constructor() {} getData(@logParams("xxx") uuid: any) { console.log(uuid); } } var http: any = new HttpClient(); http.getData(123); console.log(http.apiUrl);
装饰器执行顺序
- 属性装饰器 -> 方法装饰器 -> 方法参数装饰器 -> 类装饰器
- 同一个方法有多个装饰器,会像剥洋葱一样,先从外到内进入,然后由内向外执行。
function logClass1(params: string) { return function (target: any) { console.log("类装饰器1"); }; } function logClass2(params: string) { return function (target: any) { console.log("类装饰器2"); }; } function logAttribute(params?: string) { return function (target: any, attrName: any) { console.log("属性装饰器"); }; } function logMethod(params?: string) { return function (target: any, attrName: any, desc: any) { console.log("方法装饰器"); }; } function logParams1(params?: string) { return function (target: any, attrName: any, desc: any) { console.log("方法参数装饰器1"); }; } function logParams2(params?: string) { return function (target: any, attrName: any, desc: any) { console.log("方法参数装饰器2"); }; } @logClass1("zhainanya-1") @logClass2("zhainanya-2") class HttpClient { @logAttribute() public apiUrl: string | undefined; constructor() {} @logMethod() getData() { return true; } setData(@logParams1() attr1: any, @logParams2() attr2: any) {} } var http: any = new HttpClient();
其他知识点
- 类型别名,联合类型,类型断言,声明文件 || 第三方模块
类型别名
// type aliases 类型别名
type PlusType = (x: number, y: number) => number;
function sum(x: number, y: number) {
return x + y;
}
const sum2: PlusType = sum;
————
type FetchKeys = (data:any[]) => Promise<string[]>;
interface FetchKeys {
(data:any[]): Promise<string[]>
}
联合类型
// 联合类型
type NameResolver = () => string;
type NameOrResolver = string | NameResolver;
function getName(n: NameOrResolver): string {
if (typeof n === "string") {
return n;
} else {
return n();
}
}
// type assertion 不确定类型,就访问其中的类型或方法
类型断言
//类型断言 -》 不是类型转换 断言成联合类型里没有的会报错
function getLength(input: string | number): number {
// const str = input as String; //interface
// if (str.length) {
// return str.length;
// } else {
// const number = input as Number;
// return number.toString().length;
// }
if ((<string>input).length) {
return (<string>input).length;
} else {
return input.toString().length;
}
}
声明文件
// 声明文件 declare 同级文件 jQuery.d.ts
declare var jQuery: (selector: string) => any;
第三方模块
// 第三方模块
// npm install --save @types/jquery
*/
实用工具类型
- 实用工具类型
- TypeScript 提供了几种实用程序类型来促进常见的类型转换
常见问题
window 添加自定义属性
- global.d.ts文件
declare global {
interface Window {
RM_FONT_FACE: string;
RM_ANIMATION: string;
}
}
export {};
interface 和 type 区别
TypeScript 中的 interface 和 type 都可以用来定义对象的形状或者函数签名,但它们在某些方面有所不同。下面是 interface 和 type 的一些主要区别:
- 扩展性(Extensibility)
interface:可以被扩展以构建新的接口。通过使用 extends 关键字,接口可以扩展一个或多个现有的接口。
type:不可以被扩展,但可以通过交叉类型(使用 & 操作符)来组合现有的类型。 - 重复声明(Duplicate Declarations)
interface:可以在同一作用域内多次声明同一个接口,其成员会自动合并。
type:不允许同名的重复声明。 - 使用场景(Use Cases)
interface:推荐在定义对象的形状或实现类的场景中使用。它支持声明合并,这在声明文件(例如 .d.ts 文件)或声明类和函数库时十分有用。
type:适用于定义联合类型、交叉类型、元组类型等复杂类型。类型别名可以声明几乎任何类型,包括基本类型、联合类型和交叉类型。 - 实现(Implementation)
interface:可以通过类来实现(使用 implements 关键字)。
type:不能被类实现,因为它们可以代表非对象类型(如原始类型、联合和交叉类型)。 - 性能(Performance)
interface:在某些情况下,interface 可能会得到更好的性能,因为它们更容易被TypeScript编译器优化。
type:由于类型别名可能会直接展开,所以在某些复杂的类型组合中可能会导致编译器和IDE性能下降。