类
多态、继承
父类定义一个方法不去实现,让继承它的子类去实现,每一个子类有不同的表现。
class Animal {
name:string
constructor(name:string){
this.name = name
}
getVariety(){} //父类定义了getVariety方法不去实现
}
class Cat extends Animal{
constructor(name:string){
super(name)
}
getVariety():string{
return `${this.name}是只猫!` //子类Cat对getVariety的实现
}
}
class Dog extends Animal{
constructor(name:string){
super(name)
}
getVariety():string{
return `${this.name}是只狗!` //子类Dog对getVariety的实现
}
}
//这种子类继承父类后重写了getVariety方法,每个子类有自己不同的实现,叫多态
抽象类和抽象方法
TS中的抽象类是提供其他类继承的基类,不能直接实例化。用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。
抽象类和抽象方法用来定义标准,来约束子类必须实现指定方法
abstract class Animal {
name:string
constructor(name:string){
this.name = name
}
abstract getVariety():any
run():void{} //没有abstract标识的方法在子类中可以不实现
}
// const animal = new Animal() //报错!!抽象类无法实例化
class Dog extends Animal{
constructor(name:string){
super(name)
}
getVariety(){ //实现抽象方法中的getVariety方法,如果没有则报错!如果要标明返回值类型,必须和抽象方法中一致
return `${this.name}是只狗!`
}
}
const dog = new Dog('旺财')
console.log(dog.getVariety()) //旺财是只狗!
abstract class Animal1 {
name:string;
constructor(name:string) {
this.name=name;
}
run():void{};
seep():void{
alert(`${this.name}睡觉睡的很香`)
}
abstract eat():any;
}
class Cat extends Animal1 {
constructor(name:string) {
super(name)
}
eat():void{
alert(`${this.name} 吃小鱼哦哦哦`)
}
run():void{
console.log(`${this.name} 跑的飞快哦`)
}
}
let c=new Cat("大花猫")
c.eat()
c.run()
console.log('>>>>>>>>>>>>',c.name);
c.seep()
type
类型别名
使用 type 创建为类型创建别名,常用于联合类型
type Name = string; //为string创建别名Name
type NameResolver = () => string; //TS中=>左边是参数,右边是返回值,所以类型是一个 返回字符串的函数,且该函数没有参数
type NameOrResolver = Name | NameResolver; // 为联合类型起别名
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
} else {
return n();
}
}
interface一般表示object,class,function类型。type可以复杂类型(例如联合类型等)
type Key = string | number;
type JSXElementConstructor<P> =((props: P) => ReactElement | null) | (new (props: P) => Component<P, any>);
定义方法类型
//使用类似定义箭头函数的语法来表示函数类型的参数和返回值类型,此时=> 类型仅仅用来定义一个函数类型而不用实现这个函数。
//需要注意的是,这里的=>与 ES6 中箭头函数的=>有所不同。TypeScript 函数类型中的=>用来表示函数的定义,
//其左侧是函数的参数类型,右侧是函数的返回值类型;而 ES6 中的=>是函数的实现。
type say = (age: number) => string;
const says: say = function (age: number): string {
return String(age);
};
console.log(says(222));
//interface say1 {
// (age: number): string;
//}
//const says1: say1 = function (age: number): string {
// return String(age);
//};
//console.log(says1(222333));
字符串字面量类型
用来约束取值只能是某几个字符串中的一个。
注意:类型别名与字符串字面量类型都是使用 type 进行定义
//使用 type 定义一个字符串字面量类型 EventNames,它只能取三种字符串中的一种
type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {
// do something
}
handleEvent(document.getElementById('hello'), 'scroll'); // 没问题
handleEvent(document.getElementById('world'), 'dbclick'); // 报错,event 不能为 'dbclick'
实现
类实现interface或type
type Point = {
x: number;
aaa: (str: string) => string
}
class P2 implements Point {
x: number
constructor(x: number) {
this.x = x
}
aaa(str: string): string {
return str
}
}
继承
interface 和 type 两者都可以继承,语法不同
interface IPoint {
x: number;
}
type Point = {
x: number;
}
// 接口继承接口
interface Point2 extends IPoint {
y: number;
}
// 接口继承type
interface Point2 extends Point {
y: number;
}
//type继承接口
type Point2 = IPoint & {y: number;}
//type继承type
type Point2 = Point & {y: number}
//联合类型的type不能被接口继承 或实现
// An interface can only extend an object type or intersection of object types with statically known members
//接口只能扩展对象类型或对象类型与静态已知成员的交集
type Points = {x: number;} | {y: number;}
//可以接口继承和实现
type Points = {x: number;} & {y: number;}
extends在type中的使用
用来判断一个类型是不是可以分配给另一个类型,这在写高级类型的时候非常有用
需要理解的是,这里A extends B,是指类型A可以分配给类型B,而不是说类型A是类型B的子集
type Human = {
name: string;
}
type Duck = {
name: string;
}
type Bool = Duck extends Human ? 'yes' : 'no'; // Bool => 'yes'
//因为 Duck 没有类型为string的age属性,类型Duck不满足类型Human的类型约束
type Human1 = {
name: string;
age: string;
}
type Duck1 = {
name: string;
}
type Bool = Duck1 extends Human1 ? 'yes' : 'no'; // Bool => 'no'
继续看示例
type A1 = 'x' extends 'x' ? string : number; // string
type A2 = 'x' | 'y' extends 'x' ? string : number; // number
type P<T> = T extends 'x' ? string : number;
type A3 = P<'x' | 'y'> // ?
接口
属性接口
interface Person {
name:string;
age:number;
sex?:string;
}
function getName(info:Person){
console.log('>>>>>>>>>>>>',info);
}
getName({
name:"王小二",
age:12
})
interface Person {
name:string;
age:number;
sex?:string;
}
function getName(info:Person){
console.log('>>>>>>>>>>>>',info);
}
let obj={
name:"王小二",
age:12,
address:'芜湖市1'
}
getName(obj)
函数接口
interface Person {
(name:string,age:number):void
}
let myGetName:Person= function(name:string,age:number):void{
console.log('>>>>>>>>>>>>',name,age);
}
myGetName( "王大额热二",102)
类接口、对象接口
interface Person{
name:string;
eat():void;
}
class Mans implements Person {
name:string;
constructor(name:string) {
this.name=name
}
eat():void{
console.log('>>>>>>>>>>>>',`${this.name}吃自己`);
}
}
let m =new Mans("达到调")
m.eat()
interface aaa {
name: string;
hahaha(name: string): string;
add: (a: number, b: number) => number;
}
class Aa1 implements aaa {
constructor(name: string) {
this.name = name
}
name: string;
hahaha(name: string): string {
return name + 1;
}
add = function (a: number, b: number) {
return a + b
}
}
const Bbb: aaa = {
name!: '111',
hahaha: function (name: string): string {
return name + 1
},
add: function (a: number, b: number): number {
return a + b
}
}
const Bbb1 = <aaa>{
name: '111',
hahaha: function (name: string): string {
return name + 1
}
}
可索引接口
interface arr {
[age:number]:string
}
let arr1:arr=["1","2","3","4"]
let arr2: arr= {0: 'a',1: 'b',2: 'c'};
let arr3:Array<arr>=["1","2","3","4"]
let arr4:Array<arr>=[["1","2","3","4"]]
console.log('>>>>>>>>>>>>',arr1,arr2,arr3,arr4);
interface ArrayKeyAny1 {
[key: string]: number; // 根据需要,可把key 和 value 类型设置成指定的类型
}
const array1: ArrayKeyAny1 = { "123": 123 };
console.log("AppApp", array1[0]);
当接口指定了可索引类型后,在定义其他属性时,也必须满足可索引类型
interface qq {
[propName: string]: number;
// name: string; //这里会报错,因为不满足索引类型指定的返回值类型
}
let a: qq = {
1: 1, aa: 2
}
console.log(a[1], a.aa)
interface IPerson {
name: string
age: number
family?: string
[propName: string]: any // 一个 interface 中任意属性只能有一个,[propName: string]: string | number // 一般设置 any,因为其他类型必需是任意类型的子集
}
接口继承
interface Shape {
color: string
}
interface Triangle extends Shape {
sideNum: number
}
const triangle = <Triangle>{ color: 'blue', sideNum: 3 };
const triangle1: Triangle = { color: 'blue', sideNum: 3 };
// const triangle2: Triangle; 编译失败
// triangle2.color='dsad' 编译失败
const triangle2 = <Triangle>{};
triangle.color = "blue";
triangle.sideNum = 3;
接口合并
interface Obj {
item1: string;
item3: boolean;
}
interface Obj {
item1: string; //后续属性声明必须属于同一类型
item2: number;
}
const a12: Obj = {
item1: "123",
item2: 1234,
item3:true
};
接口扩展
interface Person{
name:string;
eat():void;
}
class Food{
foods:string;
constructor(foods:string){
this.foods=foods
}
}
class Mans extends Food implements Person {
name:string;
constructor(name:string,foods:string) {
super(foods)
this.name=name
}
eat():void{
console.log('>>>>>>>>>>>>',`${this.name}吃自己和${this.foods}`);
}
}
let m =new Mans("小月月","葡萄")
m.eat()
泛型
泛型接口
interface user1 {
<T>(name: T): T
}
let myName1: user1 = function <T>(name: T): T {
alert(name);
return name
}
myName1<string>("你好啊大萨达")
interface user2<T> {
(name: T): T
}
let myName2: user2<string> = function <T>(name: T): T { //把泛型参数提前到接口名
alert(name);
return name
}
myName2("丰东股份的喝")
泛型函数
function getName<T>(name:T):T{
return name
}
alert(getName<number>(23))
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
swap<number, string>([7, 'seven']); // ['seven', 7]
泛型类
class User<T>{
name:T;
constructor(name:T){
this.name=name
};
add():T{
alert(this.name)
return this.name
}
}
let us = new User<number>(0)
us.add()
class Animal<T> {
//此处的泛型为限制构造函数参数类型以及类成员变量的类型
name: T;
constructor(name: T) {
this.name = name;
}
action<T>(say: T): T {
//此处为限制类中成员方法action, 若此处不写<T>则使用类声明的<T>
console.log("action", say);
return say;
}
}
const dog = new Animal<string>("dog"); //此处的striing为限制构造函数参数类型以及类 成员变量的类型
dog.action(123); //此处为限制类中成员方法 action
泛型约束
当对泛型进行约束时,即只允许这个函数传入那些包含 length 属性的变量
interface Lengthwise { //定义一个接口
length: number;
str:string
}
// 使用了 extends 约束了泛型 T 必须符合接口 Lengthwise 的形状,即必须包含 length 属性,不包含此属性就会报错
function loggingIdentity<T extends Lengthwise>(arg: T): T { //泛型T继承接口
console.log(arg.length);
return arg;
}
const adsad={
length:5,
str:'dasd'
}
loggingIdentity<Lengthwise>(adsad)
甚至有多个多个类型参数时,类型参数之间也可以约束
// 使用两个类型参数,其中要求 T 继承 U,这样就保证了 U 上不会出现 T 中不存在的字段。
function copyFields<T extends U, U>(target: T, source: U): T {
for (let id in source) {
target[id] = (<T>source)[id];
}
return target;
}
let x = { a: 1, b: 2, c: 3, d: 4 };
copyFields(x, { b: 10, d: 20 });
类类型作为泛型
class User{
name:string | undefined;
age:number | undefined;
status: string | undefined;
constructor(info:{
name:string | undefined,
age:number | undefined,
status?: string | undefined,
}){
this.name=info.name
this.age=info.age
this.status=info.status
}
}
class MysqlEditUser<T> {
constructor() {
}
add(info:T):boolean{
console.log(info)
return true
}
}
let u = new User({
name:'大牛',
age:22,
status:'4545'
})
let mysql=new MysqlEditUser<User>()
mysql.add(u)
泛型参数的默认类型
// 为泛型T指定默认泛型参数为string
function createArray<T = string>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
声明合并
函数的合并(重载)
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
接口的合并
接口中的属性在合并时会简单的合并到一个接口中;方法合并与函数的合并(函数重载)一样
interface Alarm {
price: number;
alert(s: string): string;
}
interface Alarm {
price: number; //两个相同属性名合并,类型必须是唯一的(类型必须相同)
weight: number;
alert(s: string, n: number): string;
}
// 相当于:是叠加,不是覆盖
interface Alarm {
price: number;
weight: number;
alert(s: string): string;
alert(s: string, n: number): string;
}
in操作符
in的定义是:用于遍历枚举类型。
注意:in只能遍历枚举类型,不能遍历对象(keyof遍历的是对象(类型))。
type Keys = 'a' | 'b';
type Obj = {
[p in Keys]: any
} // {a: any, b: any}
keyof
首先来看keyof的定义:keyof操作符可以用来枚举出一个对象中的所有key值。
通俗来说,keyof可以取出一个对象中的所有由key值组成的联合类型。
实际效果类似于Object.keys()
//keyof P 返回的是P的属性名组成的联合类型
interface Point {
name: string;
age: number;
gender: string;
}
type Key = keyof Point // 返回的是'name' | 'age' | 'gender'
解决对象使用 [变量] 的调用方式
interface Obj {
item1: string;
item2: number;
}
const a12: any = {
item1: "123",
item2: 1234,
};
//let key2: string = "item1" // 没有给key 的类型进行定义,故key可以是any 类型
//console.log( a12[key2]); // 报错,因为 key 可以是任何值,故会报错。
const key11: keyof Obj = "item1"; //把key 设置为 Obj 的key 的联合类型,即: "item1" | "item2"
console.log("a12[key1]", a12[key11]); //正确,不会报错
type dsaa = {
name: string
sex: string
age: number
}
let ccda: keyof dsaa = 'name'
console.log("ccda", ccda)//ccda name
注意 keyof Obj 中的 Obj 是一个接口或者自定义type,若Obj是对象则需要使用keyof typeof Obj
keyof在函数中使用
// 若声明了对象
const a = {
item1: "xxx1",
item2: "xxx2",
};
function getValue(arg: keyof typeof a) {
return a[arg];
}
console.log(getValue("item1"));
// 若声明了接口
interface A {
item1: string;
item2: string;
}
// 则 函数的参数类型可以这样写
function getValue1(arg: keyof A) {
return a[arg];
}
console.log(getValue1("item2"));
返回T中K存在的变量值
function pluck<T, K extends keyof T>(o: T, names: Array<K>): T[K][] {
return names.map(n => o[n]);
}
interface T {
name: string
age: number
}
type K = 'name' | "age"
let a1 = {
name: "dasf",
age: 2
}
let a2: Array<K> = ['name']
console.log("pluck(a1, ['name'])", pluck<T, K>(a1, a2))
//输出 pluck(a1, ['name']) ['dasf']
泛型工具
keyof 产生联合类型, in 则可以遍历枚举类型
interface Person {
name: string;
age: number;
}
type K1 = keyof Person; // "name" | "age"
//可以看到keyof Person返回的枚举类型。 "name" | "age"
let a1: { [key in K1]: string }
type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join"
let a2: { [key in K2]: string }
type K3 = keyof { [x: string]: Person }; // string | number
let a3: { [key in K3]: string }
Exclude
Exclude是TS中的一个高级类型,其作用是从第一个联合类型参数中,将第二个联合类型中出现的联合项全部排除,只留下没有出现过的参数。
type Exclude<T, U> = T extends U ? never : T;
type A = Exclude<'key1' | 'key2', 'key2'>
//这个定义就利用了条件类型中的分配原则,来尝试将实例拆开看看发生了什么:
type A = `Exclude<'key1' | 'key2', 'key2'>`
// 等价于
type A = `Exclude<'key1', 'key2'>` | `Exclude<'key2', 'key2'>`
// =>
type A = ('key1' extends 'key2' ? never : 'key1') | ('key'2 extends 'key2' ? never : 'key2')
// =>
// never是所有类型的子类型
type A = 'key1' | never
Partial
Partial 作用是将传入的属性变为可选项.
interface T1 {
name: string
age: number
}
//keyof T 拿到 T 所有属性名, 然后 in 进行遍历, 将值赋给 P, 最后 T[P] 取得相应属性的值
type Partial<T> = {
[P in keyof T]?: T[P];
};
let aaa: Partial<T1> = {
name: "lps3",
age: 122,
}
console.log("aaa", aaa)
//let aaa: {
// [p in keyof Partial<T1>]: T1[p]
//} = {
// name: "lps3",
// age: 122,
//}
Required
Required 的作用是将传入的属性变为必选项, 源码如下
interface T2 {
name?: string
age: number
}
//我们发现一个有意思的用法 -?, 这里很好理解就是将可选项代表的 ? 去掉, 从而让这个类型变成必选项,
//与之对应的还有个+? , 这个含义自然与-?之前相反, 它是用来把属性变成可选项的
type Required<T> = {
[P in keyof T]-?: T[P]
};
let bbb: Required<T2> = {
name: "lps3",
age: 122,
}
console.log("bbb", bbb)
[P in keyof T]?: T[P]:可选
[P in keyof T]+?: T[P]:可选,等价于?
[P in keyof T]-?: T[P]:必选
[P in keyof T]: T[P]:可选的依然可选,必选的依然必选
Pick
从T中提取出T与K属性值相同的属性
//假设Person类型中有name、age、sex属性,当我们想生成一个新的类型只支持name、age时,则可使用Pick
type Pick<T, K extends keyof T> = {
[P in K]: T[P]
};
interface Person {
name: string,
age: number,
sex: string,
}
let person: Pick<Person, 'name' | 'age'> = {
name: '小王',
age: 21,
}
声明文件
声明文件以 .d.ts 为后缀
装饰器
/**
* 装饰器
*/
//装饰器的执行顺序 说的是不是有问题?,属性、方法、参数装饰器的执行顺序是按照其在类内部的位置,依次向下执行,同一个属性、方法、参数有多个装饰器的时候,
//其上的装饰器从后往前执行,类装饰器总是在最后;当类有多个装饰器的时候也是从下往上(后前)执行,这样总结会不会更准确些?
//而且当一个方法既有方法装饰器也有参数装饰器,是先执行参数装饰器(多个的时候从后往前执行),在执行方法装饰器(多个的时候从后往前执行)
/**
* @param target 类装饰器
*/
function logClass1(target:any){
console.log('>>>>>>>>>>>>logClass1',target);
}
function logClass2(params:any){
return function(target:any){
console.log('>>>>>>>>>>>>logClass2<<<<<<<<<<<<<<<<<<<<<<<<<');
// console.log('>>>>>>>>>>>>attr',target);
// console.log('>>>>>>>>>>>>params',params);
}
}
/**
* @param target 属性装饰器
*/
function logAttr(params:any){
return function (target:any,attr:any){
console.log('>>>>>>>>>>>>logAttr<<<<<<<<<<<<<<<<<<<<<<<<<');
// console.log('>>>>>>>>>>>>params',params);
// console.log('>>>>>>>>>>>>target',target);
// console.log('>>>>>>>>>>>>attr',attr);
}
}
/**
* @param target 函数(方法)装饰器
*/
function logMethod(params:any){
return function (target:any,methodName:any,methodDesc:any){
console.log('>>>>>>>>>>>>>>>>>logMethod<<<<<<<<<<<<<<<<<<<<<<<<<<<');
// console.log('>>>>>>>>>>>>params',params);
// console.log('>>>>>>>>>>>>target',target);
// console.log('>>>>>>>>>>>>methodName',methodName);
// console.log('>>>>>>>>>>>>methodDesc',methodDesc);
}
}
/**
* 函数(方法)参数装饰器
*/
function logCanshu1(params:any){
return function (target:any,methodName:any,index:any) {
console.log('>>>>>>>>>>>>>>>>>logCanshu1<<<<<<<<<<<<<<<<<<<<<<<<<<<');
// console.log('>>>>>>>>>>>>params',params);
// console.log('>>>>>>>>>>>>target',target);
// console.log('>>>>>>>>>>>>methodName',methodName);
// console.log('>>>>>>>>>>>>index',index);
}
}
function logCanshu2(params:any){
return function (target:any,methodName:any,index:any) {
console.log('>>>>>>>>>>>>>>>>>logCanshu2<<<<<<<<<<<<<<<<<<<<<<<<<<<');
// console.log('>>>>>>>>>>>>params',params);
// console.log('>>>>>>>>>>>>target',target);
// console.log('>>>>>>>>>>>>methodName',methodName);
// console.log('>>>>>>>>>>>>index',index);
}
}
@logClass1
@logClass2("传入的类装饰器参数")
class Person{
@logMethod('传入方法的参数1')
setName(@logCanshu1("传入参数1的") cnashu1:any,@logCanshu2("传入的参数2") canshu2:any){
}
@logAttr('传入属性1的参数')
names:string | undefined;
constructor() {
}
@logMethod('传入方法的参数2')
getNames(...res:any[]){
console.log('>>>>>>>>>>>>getNames<<<<<<<<<<<<',res);
}
@logAttr('传入属性2的参数')
urls:string | undefined;
}
let p=new Person();
// function logClass1(params:string){
// return function(target:any){
// console.log('类装饰器1')
// }
// }
// function logClass2(params:string){
// return function(target:any){
// console.log('类装饰器2')
// }
// }
// function logAttribute1(params?:string){
// return function(target:any,attrName:any){
// console.log('属性装饰器1')
// }
// }
// function logAttribute2(params?:string){
// return function(target:any,attrName:any){
// console.log('属性装饰器2')
// }
// }
// function logMethod1(params?:string){
// return function(target:any,attrName:any,desc:any){
// console.log('方法装饰器1')
// }
// }
// function logMethod2(params?:string){
// return function(target:any,attrName:any,desc:any){
// console.log('方法装饰器2')
// }
// }
// 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('http://www.itying.com/api')
// @logClass2('xxxx')
// class HttpClient{
// constructor(){
// }
// @logMethod1()
// @logMethod2()
// getData(){
// return true;
// }
// @logAttribute1()
// @logAttribute2()
// public apiUrl:string | undefined;
// @logMethod1()
// @logMethod2()
// setData(@logParams1() attr1:any,@logParams2() attr2:any,){
// }
// }
// var http:any=new HttpClient();