类型推论
鼠标悬停之后,会显示其返回值类型推论
类型兼容
interface IA {
a: number;
}
var a: IA;
var b: { a: number, b: number } = { a: 1, b: 2 };
a = b
//向下兼容-->少的兼容多的
//多的兼容少的需要加断言
//类的兼容,看里面的属性和方法
高级类型
交叉类型 &
`交叉类型是将多个类型合并为一个类型`
//把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性
interface IA {
a: number;
}
interface IB {
b: number;
}
var a: IA & IB = { a: 1, b: 2 };
console.log(a)//{a: 1, b: 2}
interface IA {
a: number;
b: number;
}
interface IB {
c: number;
b: number;
}
//两者都有b 写一个就行
var o: IA & IB = { a: 1, b: 2, c: 3 }//{a: 1, b: 2, c: 3}
`对交叉类型 详解`
function extend<T, U>(first: T, second: U): T & U {
//设置o的类型是T & U
var o: T & U = {} as T & U;
//key 为对象中的属性 类型是T
var key: keyof T;
for (key in first) {
//把first中的属性赋值给o
(<T>o)[key] = first[key];
}
//key1 为对象中的属性 类型是U
var key1: keyof U
for (key1 in second) {
//判断key1 是否是o中的属性
if (!Object.hasOwn(<object>o, key1))
//若不是 就把second中对应key1的属性放到o中
(<U>o)[key1] = second[key1]
}
return o;
}
联合类型 |
//联合类型表示一个值可以是几种类型之一。 我们用竖线( |)分隔每个类型,所以 number | string | boolean表示一个值可以是 number, string,或 boolean
interface IA {
a: number;
c: number;
}
interface IB {
b: number;
d: number;
}
var a: IA | IB = { a: 1, c: 2 }
a = { d: 1, b: 2 }
a = { a: 1, b: 2, c: 3 }
//至少要有其中一种类型里的全部的属性
a = { a: 1, b: 2 };//错误
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
//设置了该函数的返回值是Bird或者Fish
function getSmallPet(): Bird | Fish {
return <Bird | Fish>{}
}
var o = getSmallPet();
//因为Bird和Fish中都有layEggs方法所以不需要加断言
o.layEggs()
//错误
// if (o.fly) {
// o.fly();
// }
// 下面这种写法需要每个部分都要断言
if ((<Bird>o).fly) {
(<Bird>o).fly()
}
类型保护
// 判断当前元素是否是某个类型,以让后面的内容都设置该类型
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
function getSmallPet(): Bird | Fish {
return <Bird | Fish>{}
}
var o = getSmallPet();
//封装成一个函数进行使用
function isBird(o: Bird | Fish): o is Bird {
return (<Bird>o).fly !== undefined;
}
function isFish(o: Bird | Fish): o is Fish {
return (<Fish>o).swim !== undefined;
}
//在判断的时候就不需要使用泛型了
if (isBird(o)) {
o.fly();
} else {
o.swim();
}
if (isFish(o)) {
o.swim();
} else {
o.fly();
}
//小案例 改变div的颜色
var div: HTMLDivElement = document.querySelector("div") as HTMLDivElement;
div.addEventListener("click", clickHandler);
function isHTMLElement(o: any): o is HTMLElement {
return o instanceof HTMLElement;
}
function clickHandler(e: MouseEvent): void {
if (isHTMLElement(e.target)) {
if (e.target.nodeName != "DIV") return;
e.target.style.backgroundColor = "blue";
}
}
类型别名
// 给string | number这个类型起了一个A的别名
type A = string | number;
var o: A = "a";
o = 1;
interface IA {
a: number;
b: number;
}
//给接口IA起了一个A的别名
type A=IA;
//接口可以继承接口也可以继承别名
interface IB extends A {
c: number;
}
var o: IB = {
a: 1,
b: 2,
c: 3
}
//别名不能继承接口
interface IA{
a:number;
b:number;
}
type A={c:number} extends IA//错误
`类型别名有类似于接口的特征`
type A={a:number,b:number};
var o:A={a:1,b:2};
interface IA {
a: number;
b: number;
}
var a: IA // 鼠标放在这个类型上,接口看不到具体内容
type A = { a: number, b: number };
var o1: A // 鼠标放在这个类型上,别名可以看到具体的内容
使用
`作为值的选项`
type MouseEvent1 = "click" | "mousedown" | "mouseup" | "mousemove";
var a: MouseEvent1;
// if (a === "click") {
// }
switch (a) {
//每次使用一个都会少一个
case "click":
break;
case "mousedown":
break;
case "mousemove":
break;
case "mouseup":
break;
}
预定义的有条件类型
1.Exclude<T, U> -- 从T中剔除可以赋值给U的类型。
2.Extract<T, U> -- 提取T中可以赋值给U的类型。
3.NonNullable<T> -- 从T中剔除null和undefined。
4.ReturnType<T> -- 获取函数返回值类型。
5.InstanceType<T> -- 获取构造函数类型的实例类型。
使用
type A = "click" | "mousedown" | "mousemove" | "mouseup" | "mouseover" | "mouseout";
type B = "mouseover" | "mouseout" | "mouseenter" | "mouseleave";
//这个把 A与B中相同的给剔除了 返回A中剩余的值
type C = Exclude<A, B>;
var c1: C = ""
console.log(c1)
索引
//索引 用来取值
function fn<T, K extends keyof T>(o: T, key: K): void {
console.log(o[key])
}
//去类型的方法
interface IA {
a: number;
b: number;
}
type a = {
readonly [P in keyof IA]: IA[P]
}
命名空间 namespace
随着更多验证器的加入,我们需要一种手段来组织代码,以便于在记录它们类型的同时还不用担心与其它对象产生命名冲突。 因此,我们把验证器包裹到一个命名空间内,而不是把它们放在全局命名空间下。
namespace Rect {
export class Box {
constructor() {
}
public play(): void {
console.log("play");
}
}
}
namespace Circle {
export class Box {
constructor() {
}
public play(): void {
console.log("play1")
}
}
}
var a: Rect.Box = new Rect.Box();
var b: Circle.Box = new Circle.Box();
a.play();
b.play();
`外部枚举 declare`
//在空间前加declare 当空间当前文件外部时使用不需要导入
//A.ts
declare namespace Box {
export interface IA {
a: number;
b: number;
}
}
//B.ts
var a: Box.IA = { a: 1, b: 2 };
console.log(a);//{ a: 1, b: 2 }
装饰器
装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上。
装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入
都是直接设置到原型链中
类装饰器
function fn(className: { new(): Box }) {
className.prototype.run = function () {
console.log("run");
}
}
@fn
class Box {
constructor() {
}
public play(): void {
console.log("play")
}
}
`装饰器实现类的继承`
function addWalk(className: { new(): Box | Ball }) {
className.prototype.walk = function () {
console.log("walk")
}
}
interface IWalk{
walk():void;
}
@addWalk
class Box {
constructor() {
}
run() {
}
}
@addWalk
class Ball {
constructor() {
}
play() {
}
}
var a: Box = new Box();
var b: Ball = new Ball();
//调用添加的addWalk方法
(<Box & IWalk>a).walk();
(<Ball & IWalk>b).walk()
console.log(a)
console.log(b);
`一个类可以加多个装饰器`
`类装饰器可以设定泛型和调用类自身的方法`
function addRun<T>(className: { new(): T }) {
className.prototype.run = function () {
//调用当前类的自身方法
this.play();
// console.log(this) 实例化对象
console.log("run")
}
}
interface IRun {
run(): void;
}
@addRun
class Box {
constructor() {
}
public play() {
console.log("play");
}
}
@addRun
class Ball {
constructor() {
}
public play() {
console.log("play2")
}
}
var a: Box = new Box();
var b: Ball = new Ball();
(<Box & IRun>a).run();
(<Ball & IRun>b).run();
console.log(a)
console.log(b)
属性装饰器
function format(arg: string) {
return function (thisArg: Box, prototypeName: keyof Box) {
// thisArg[prototypeName] = "aaa"
thisArg[prototypeName] = arg;
thisArg.arg = 30;
}
}
class Box {
@format("aaa")
public name: string = "a";
[key: string]: string | number;
constructor() {
}
}
var b: Box = new Box();
console.log(b)
类的方法的装饰器
//给类的方法中添加新内容
function supers() {
return function (thisArg: Box, medthodName: keyof Box, valueDesc: PropertyDescriptor) {
//valueDesc.value 是play方法
var fn = valueDesc.value;
// thisArg是原型链原型对象
// valueDesc 就是当前装饰方法,现在重写play方法
valueDesc.value = function () {
console.log("begin Play")
// console.log(this);//这个this是实例化对象的this
fn.call(this)
}
}
}
class Box {
public age: number = 30;
constructor() {
}
@supers()
public play(): void {
console.log("play" + this.age)
}
}
var b: Box = new Box();
b.play();
console.log(b)
参数的修饰器
//参数的修饰器
function setArg(n: number) {
return function (thisArg: Box, medthodName: keyof Box, value: number) {
// thisArg是原型链本身
thisArg.age = n;
}
}
class Box {
[key: string]: any;
constructor() {
}
play(@setArg(10) num: number): void {
console.log(num)
console.log(this.age)
}
}
var b: Box = new Box();
b.play(2)
console.log(b)
this总结
1、全局中this
console.log(this);//window
2、函数中的this
//ES6严格模式 undefined 非严格模式window
function fn(this:void){
console.log(this);
}
var obj={
play(){
function fn(this:void){
console.log(this)
}
fn()
}
}
obj.play()
3、对象中this
var b=3;
var o={
b:2,
// 对象属性上的this等于对象外的this指向
a:this.b,
c(){
// 这个this指向当前对象
console.log(this)
},
d:function(){
// 这个this指向当前对象
console.log(this);
}
}
4、普通回调函数中的this
//普通回调函数中 严格模式时this指向undefined,非严格模式this指向window
function fn(f:()=>void):void{
f();
}
function fn1(this:void):void{
console.log(this)
}
fn(fn1);
setTimeout(function(this:void){
console.log(this);
})
var arr=[1,2,3,4];
arr.forEach(function(this:void){
console.log(this)
})
5、arguments回调函数中
//使用arguments执行回调函数,被调用的函数中this指向调用该函数所在上下文环境中arguments
function fn(f:()=>void){
arguments[0]();
}
function fn1(this:void){
console.log(this)
}
fn(fn1);
6、事件回调函数
//事件回调函数中this指向当前侦听事件的对象
document.addEventListener("click",clickHandler);
function clickHandler(this:void,e:MouseEvent){
console.log(this);
}
7、数组的部分遍历方法中有thisArg参数的回调函数
//当数组部分遍历方法的回调函数中使用this,这个this指向遍历方法的第二个参数thisArg
var arr=[1,2,3,4];
arr.forEach(function(this:void){
console.log(this)
},{a:1})
arr.forEach()
arr.map();
arr.filter();
arr.find();
arr.findIndex();
arr.findLastIndex();
arr.findLast();
arr.every();
arr.some();
arr.flatMap();
8、箭头函数
var o = {
play() {
var obj = {
a: () => {
console.log(this);//箭头函数外this的指向
}
}
obj.a();
}
}
o.play();
var o = {
play() {
var arr = [1, 2, 3];
arr.forEach(() => {
console.log(this);//o
})
}
}
9、call。apply,bind
使用call,apply,bind执行时带入的第一个参数是thisArg
//执行函数时,如果非严格模式时
1.thisArg是引用类型的,this直接指向thisArg,
2.如果是非引用类型的,则先转换为引用类型的该类型,this指向转后引用类型
3.如果是undefined或者null,则this指向window
//如果是严格模式时,thisArg带入是什么值,this就指向这个值
function fn(){
console.log(this)
}
fn.call({a:1});
fn.apply({a:1});
fn.bind({a:1})()
10、原型链中this指向
//如果函数使用new执行时,这个函数就是构造函数,构造函数中的this指向创建的实例化对象
function Box(this:void){
console.log(this)
}
// 在原型中this指向当前类创建的实例化对象
Box.prototype.play=function(){
console.log(this)
}
// 在类静态方法中,函数的方法就是类的静态方法,这里的this指向当前函数Box
Box.run=function(){
console.log(this)
}
var c:Box=new Box();
11、ES6 类中的this
class Box{
public a:number=1;
// 实例化属性中如果使用this指向当前实例化对象本身
public c:number=this.a;
// 静态属性中如果使用this,指向当前类Box
public static b:number=2;
public static d:number=this.b;
constructor(){
// 构造函数中this指向当前实例化对象
console.log(this)
}
public play(){
// 实例化方法中this指向当前实例化对象
console.log(this)
}
public static run(){
// 静态方法中this指向当前类
console.log(this)
}
}