基础类型定义
string/number/boolean/null/undefined/void/symbol
const num: number = 123
const name : string = "typescrit"
字面量
字面量的意思就是直接声明, 而非new关键词实例化出来的数据。
// 字面量
const n:number = 123;
const s:string = '456';
const o:object = {a:1,b:'2'};
// 非字面量
const n:Number = new Number(123);
const s:String = new String('456');
const o:Object = new Object({a:1,b:'2'});
通过上面的例子, 大家应该看明白为什么ts中有些类型的开头字母是小写的。这是因为ts中用小写字母开头的类型代表字面量, 大写的是用来表示通过new实例化的数据。
类型的内容是固定的
type defType = 'a' | 'b' | 'c' | 'string';
let type1 : defType = 'b';
let type1 : defType = 'a';
类型定义
基本类型定义
const boy:{
name:string;
age:number;
// 索引签名
[props:sting]:string;
} = {
name:"tom",
age: 12
}
定义数组类型
const numArray: number[] = [1,2,3]
const strArray: Array<string> = ["str1", "str2"]
定义函数,约束参数类型
const getTotal:() => number = () =>{ return 123; }
// 定义函数类型的接口
interface SearchFunc{
(source:string, subString:string) : boolean;
}
let search:SearchFunc;
mySearch = function(source: string, substring:string):boolean{
let result = source.search(substring)
return result > -1
}
定义接口
接口可以实现一个对象包含多个类型。
interface IBoy {
name:sting;
age: number;
}
let xiaoming:IBoy ={
name: "xiaoming",
age: 16
}
类实现接口,约束实例属性和方法
// 定义类接口,此接口约束的实例属性和方法。约束静态属性和方法需要用构造器接口
interface AnimalInterface {
name: string;
legs: number;
wings?: number;
call(sound: string): void;
}
// 类实现接口,类就必须有接口的属性和方法
class Dog implements AnimalInterface {
legs: number = 0;
constructor(public name: string, legs: number) {
this.name = name;
this.legs = legs;
}
call(sound: string): void {
sound = '旺 旺 旺'
}
}
class Bird implements AnimalInterface {
constructor(public name: string, public legs: number, public wings: number) {
this.legs = legs;
this.wings = wings;
}
call(sound: string): void {
sound = '唧 唧 唧 唧'
}
}
let wangcai = new Dog("旺财", 4)
let bage = new Bird("八哥", 2, 2)
类约束静态属性和方法,定义构造器接口
// 定义类接口,此接口约束的实例属性和方法。约束静态属性和方法需要用构造器接口
interface AnimalInterface {
name: string;
legs: number;
wings?: number;
call(sound: string): void;
}
// 定义构造器接口
interface AnimalConstructorInterface {
new(name: string, legs: number, wings?:number): AnimalInterface;
}
// 创建动物工厂方法
function createAnimal(constru:AnimalConstructorInterface,name: string, legs: number, wings?:number): AnimalInterface{
// 这里就可以约束构造器的属性
return new constru(name, legs, wings);
}
// 类实现接口,类就必须有接口的属性和方法
class Dog implements AnimalInterface {
legs: number = 0;
constructor(public name: string, legs: number) {
this.name = name;
this.legs = legs;
}
call(sound: string): void {
sound = '旺 旺 旺'
}
}
class Bird implements AnimalInterface {
name:string = 'Bird';
legs:number = 0;
// 对构造器的属性会进行强制约束
constructor() {
}
call(sound: string): void {
sound = '唧 唧 唧 唧'
}
}
let wangcai = createAnimal(Dog, "旺财", 4)
let bage = createAnimal(Bird, "八哥", 2, 2)
接口和接口的继承
interface Shape{
color:string;
}
interface Points{
num:number;
size:number;
}
interface Square extends Shape, Points{
area: number
}
// let s = {} as Square
let s = <Square>{}
s.color = 'red'
s.num = 3;
接口和类的继承
class Component {
private state:string;
}
interface SelectableComponent extends Component{
select():void;
}
class ButtonComponent extends Component implements SelectableComponent{
// ButtonComponent实现了SelectableComponent接口,则必须实现接口中的方法。
select():void{}
}
class TextComponent extends Component{
select():void{}
}
// InputComponent出现类型错误,InputComponent没有继承Component,此处没继承到state属性,会出现错误
class InputComponent implements SelectableComponent{
select():void{}
}
函数参数是对象
function add({ first = 1, second = [0] }: { first?: number; second?: number[]; }): number {
return first + second.length;
}
console.log(add({ first: 1, second: [1, 2, 3] })); //4
TS类型保护机制
有时候在代码中多处添加类型断言,明确告知TS编译器的类型,会造成代码可读性低,增加维护难度。可以使用类型保护机制。有以下四种方法
// 共用的变量
class Java {
helloJava() {
console.log("Hello Java")
}
}
class JavaScript {
helloJavaScript() {
console.log("Hello JavaScript")
}
}
// ts类型保护
enum Type { Strong, Week };
function getLanguage(type: Type, x: string | number = "default") {
let lang = type === Type.Strong ? new Java() : new JavaScript()
// ts中报错,无法识别属性helloJava
if(lang.helloJava){
lang.helloJava()
}else{
lang.helloJavaScript()
}
return lang
}
一:使用instanceof关键字创建区块
function getLanguage(type: Type, x: string | number = "default") {
let lang = type === Type.Strong ? new Java() : new JavaScript()
if(lang instanceof Java) {
lang.helloJava()
}else{
lang.helloJavaScript()
}
return lang
}
二:使用in 关键字
function getLanguage(type: Type, x: string | number = "default") {
let lang = type === Type.Strong ? new Java() : new JavaScript()
if("helloJava" in lang) {
lang.helloJava()
}else{
lang.helloJavaScript()
}
return lang
}
三:使用typeof 判断变量的基本类型
function getLanguage(type: Type, x: string | number = "default") {
let lang = type === Type.Strong ? new Java() : new JavaScript()
if(typeof x ==="string"){
x = x.toUpperCase()
}else{
x = x.toFixed(2)
}
return { lang, x }
}
四:使用类型保护函数
类型保护函数:返回值为类型谓词,格式: 参数 + is + 类型
function isJava(lang: Java | JavaScript): lang is Java{
return (lang as Java).helloJava !== undefined
}
function getLanguage(type: Type, x: string | number = "default") {
let lang = type === Type.Strong ? new Java() : new JavaScript()
if(isJava(lang)){
lang.helloJava()
}else{
lang.helloJavaScript()
}
return lang
}
TS的高级类型-交叉类型
将多个类型合并成为一个新的类型,新类型具有所有类型的特性语法:typeA & typeB
多用于扩展类型的属性
// 交叉类型
interface InterfaceA{
methodA(): void;
}
interface InterfaceB{
methodB(): void;
}
let aAndb:InterfaceA & InterfaceB = {
methodA(){},
methodB(){}
}
console.log(aAndb)
扩展类型的属性
function mixin<T extends object, K extends object>(o1:T, o2:U):T&U{
return {...o1, ...o2}
}
let r = mixin({name:'北鸟南游', age: 3}, {address:"shanghai"})
TS的高级类型-联合类型
- ts中使用
!
表示非空断言,表示该值一定不会为空 - ts中用
?.
链式判断操作符,表示对象存在的情况下,获取对象的属性,如果对象不存在,则进行获取属性 - ts中
??
表示第一个值非null或undefined,就取第一个值。用来排除null和undefined ```typescript // ! 表示值一定存在, ele不会为null let ele: HTMLElement |null = document.getElementById(“app”) ele!.innerHTML = “hello world”
// ?.的使用,aa && aa.xx && aa.xx.yy let a = {b: ‘b’} console.log(a?.b?.c)
// ??使用 123 ?? ‘save’ // 123 null ?? ‘save’ // save
<a name="atF5W"></a>
#### 联合类型
```typescript
interface JavaInterface {
helloJava(): void;
build(): void;
}
interface JsInterface {
helloJavaScript(): void;
build(): void;
}
class Java implements JavaInterface {
static Strong:boolean = true;
helloJava() {
console.log("Hello Java")
}
build(){
console.log("build")
}
}
class JavaScript implements JsInterface {
static Strong:boolean = false;
helloJavaScript() {
console.log("Hello JavaScript")
}
build(){
console.log("build")
}
}
// ts类型保护
enum Type { Strong, Week };
function getLanguage(type: Type):JavaInterface|JsInterface {
let lang = type === Type.Strong ? new Java() : new JavaScript();
return lang
}
// getLanguage方法中lang对象为联合类型,此时lang的类型不能确定是Java还是JavaScript
// 只能使用两个对象共有的属性/方法,如:build();
// 此时调用helloJava或helloJavaScript则报错
可区分的联合类型
如果函数参数的类型是多个类型的联合类型,且多个类型间有一个共用属性,可以利用这个共用属性,创建出不同的类型保护区块
interface Square {
kind: "Square";
side: number;
}
interface Rectangle {
kind: "Rectangle";
width: number;
height: number;
}
interface Circle{
kind: "Circle";
radius: number;
}
type Shape = Square | Rectangle | Circle;
// 由于此时没有设置Circle类型的判断,如果s为Circle类型则会返回undefined。
// TS报错:Function lacks ending return statement and return type does not include 'undefined'.
function area(s: Shape): number {
switch (s.kind){
case 'Square':
return s.side * s.side
case 'Rectangle':
return s.height * s.width
}
}
类型never类型,在switch中,将未捕获到的类型赋值给never,检查s是否是never类型。
function area(s: Shape) {
switch (s.kind){
case 'Square':
return s.side * s.side
case 'Rectangle':
return s.height * s.width
// 当缺少对Circle类型判断时,说明可以进入到default,s是Circle类型,并不是设置的never类型
// TS报错:Argument of type 'Circle' is not assignable to parameter of type 'never'.
default:
return ((e: never) => {
throw new Error(e)
})(s)
}
}
正确的设置,switch分支对联合类型的所有类型都进行覆盖
function area(s: Shape) {
switch (s.kind){
case 'Square':
return s.side * s.side;
case 'Rectangle':
return s.height * s.width;
case 'Circle':
return (s.radius**2) * Math.PI;
default:
return ((e: never) => {
throw new Error(e)
})(s)
}
}
TS的高级类型-索引类型
索引类型三个概念:
- 索引类型查询操作符
- keyof T:表示类型T,所有公共属性字面量的联合类型
- 索引类型访问操作符
- T[K]:表示对象T的属性K所表示的类型
- 范型约束
- T extends U:表示范型变量可以通过继承某个类型,获得属性
索引类型查询
TypeScript 允许我们遍历某种类型的属性,并通过 keyof 操作符提取其属性的名称。keyof 操作符是在 TypeScript 2.1 版本引入的,该操作符可以用于获取某种类型的所有键,其返回类型是联合类型。
keyof 与 Object.keys 略有相似,只不过 keyof 取 interface 的键。
除了接口外,keyof 也可以用于操作类
keyof T:表示类型T的所有属性的字面量的联合类型 ```typescript // 定义一个接口Obj含有属性a,b interface obj { age: number name: string } // 定义变量key,类型为keyof Obj —> “age” | “name” let key: keyof obj = “name” // key的类型为 “age” | “name”
- T extends U:表示范型变量可以通过继承某个类型,获得属性
// keyof操作类 class Person { name: string = “tom”; }
let sname: keyof Person; sname = “name”;
//keyof 操作对象 type A = keyof {a:1,b:’123’} // ‘a’|’b type B = keyof [1,2] // ‘0’|’1’|’push’…, 获取到内容的同时,还得到了Array原型上的方法和属性
keyof 操作符除了支持接口和类之外,它也支持基本数据类型:
```typescript
let K1: keyof boolean; // let K1: "valueOf"
let K2: keyof number; // let K2: "toString" | "toFixed" | "toExponential" | ...
let K3: keyof symbol; // let K1: "valueOf"
另外一种特殊情况,定义的接口属性不定。
interface Foo {
[props: string]: string;
}
此时Foo接口被认定为属性字段全部为string,但是由于js可以通过数字和字符串访问对象属性,因此keyof Foo的结果是string | number;
let f:key Foo = 123; //此时也不报错
如果想通过一个方法getKey获取对象里的属性值,通常的写法
let Boy = {
name:"xm",
age:10
}
function getKeys(o:object, k: string){
return o[k];
}
此时在return o[k]时,TS会报错,类型检验无法通过。
为了解决这个错误,可以使用范型加索引类型
let Boy = {
name:"xm",
age:10
}
function getKeys<T extends object, K extends keyof T>(o:T, name:K): T[K]{
return o[name];
}
console.log(getKeys(Boy, "name"));
索引类型访问
语法:T[K],表示对象T的属性K的类型
interface Person {
age: number;
}
let p:Person['age']; //p:number,p的类型为数字
type Man = { name:'man'; age:12}
let m:Man['name']; //m的类型为string
范型约束
T extends U:范型T可以继承对象U的属性
function prop<T extends object, K extends keyof T>(obj: T, keys: K): T[K]{
return obj[keys];
}
TS的高级类型-映射类型
映射类型:TS允许将一个类型映射成另外一种类型,有以下Readonly、Partial、Pick三种同源映射,和Record非同源映射。
Readonly 只读
将一个接口的所有属性映射为只读
interface Obj{
a:number;
b:string;
c:boolean;
}
type ReadonlyObj = Readonly<Obj>
/* TS编译之后
type ReadonlyObj = {
readonly a: number;
readonly b: string;
readonly c: boolean;
} */
let ReadonlyObjDemo:ReadonlyObj = {
a:1,
b:"readonly",
c:true
}
ReadonlyObjDemo.a = 3;
//error Cannot assign to 'a' because it is a read-only property.
Partical 可选
将一个接口的所有属性映射为可选
type PartialObj = Partial<Obj>
/* TS编译之后
type PartialObj = {
a?: number | undefined;
b?: string | undefined;
c?: boolean | undefined;
} */
// a/b/c三个属性都是可选的
let PartialObjDemo:PartialObj = {
a:100
}
Pick 摘取部分
摘取对象的一部分属性,形成新类型
type PickObj = Pick<Obj, "a" | "b"> //抽取了Obj对象的a、b属性,形成一个新的类型
/* TS编译之后
type PickObj = {
a: number;
b: string;
} */
let pickObjDemo:PickObj = {
a:66,
b:'66'
}
非同源映射Record
引入新属性,将引入的属性的类型,设置为第二个参数。
/*
Record 源码
type Record<K extends keyof any, T> = {
[P in K]: T;
};
*/
// K为新增的属性,T为属性类型
type RecordObj = Record<"x" | "y", Obj>
// 定义一个对象包含x/y属性,这两个属性的类型为Obj类型
/* TS编译之后
type RecordObj = {
x: Obj;
y: Obj;
} */
let RecordObjDemo:RecordObj = {
x:{
a:1,
b:"x",
c:true
},
y:{
a:2,
b:"y",
c:false
},
}
TS的高级类型-条件类型
TS提供了几种内置的预定义的条件类型
- Exclude
- 用于从类型T中去除不在U类型中的成员 - Extract
- 用于从类型T中取出可分配给U类型的成员 - NonNullable
- 用于从类型T中去除undefined和null类型 - ReturnType
- 获取函数类型的返回类型 - InstanceType
- 获取构造函数的实例类型 条件类型
条件类型是由条件表达式决定的类型,使类型具有不唯一性,增加TS语言灵活性。
T extends U ? X : Y;
如果类型T可被赋值给类型U,那么结果类型是X类型,否则是Y类型。 ```typescript // 条件类型 type TypeName= T extends string ? ‘string’ : T extends number ? ‘number’ : T extends boolean ? ‘boolean’ : T extends undefined ? ‘undefined’ : T extends Function ? ‘Function’ : ‘object’
// 定义类型T1为条件类型,传入参数string,指定t1为string类型
type T1 = TypeName
![image.png](https://cdn.nlark.com/yuque/0/2021/png/737887/1622537171661-74e0384c-8456-4701-b448-71700cb87498.png#clientId=u150ec533-a6fc-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=104&id=uc5422fee&margin=%5Bobject%20Object%5D&name=image.png&originHeight=104&originWidth=395&originalType=binary&ratio=1&rotation=0&showTitle=false&size=10980&status=done&style=none&taskId=uf2a6b497-2d85-4571-bbaf-0d1aa6c3b95&title=&width=395)<br />T2传入的是数组,不在所属的条件中,则T2的类型为最后的object。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/737887/1622537204681-f0934506-efe5-4d37-ac48-13ac73725ca7.png#clientId=u150ec533-a6fc-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=110&id=uad7eae1e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=110&originWidth=420&originalType=binary&ratio=1&rotation=0&showTitle=false&size=13696&status=done&style=none&taskId=u2882d473-f9d0-4552-b1ff-4a7a6b511b6&title=&width=420)
<a name="WSEBI"></a>
#### 分步式条件类型
当T是联合类型,结果类型变为多条件类型的联合类型<br />(A | B) extends U ? X : Y;<br />可以将A和B拆解<br />(A extends U ? X : Y) | (B extends U ? X : Y)
这样定义的变量会被推断为联合类型
```typescript
type T3 = TypeName<string | string[]>
分步式条件类型应用
主要应用是对类型进行过滤
如果T可以被赋值给U,结果类型为never类型,否则为T类型
// 如果T可以被赋值给U,结果类型为never类型,否则为T类型
type Diff<T, U> = T extends U ? never : T
type T4 = Diff<'a' | 'b' | 'c', 'a' | 'x'>
拆解逻辑分析
Diff会被拆解为多个条件类型的联合类型
type Diff<T, U> = T extends U ? never : T
type T4 = Diff<'a' | 'b' | 'c', 'a' | 'e'>
// 拆解分析:
// Diff<'a', 'a' | 'e'> | Diff<'b', 'a' | 'e'> | Diff<'c', 'a' | 'e'>
// never | 'b' | 'c'
// 'b' | 'c'
- 先判断a是否可以被赋值给这个字面量联合类型’a’ | ‘e’,答案是可以的,所以返回never
- 继续,因为b不可以被赋值给字面量联合类型’a’ | ‘e’,所以返回b
- 继续,c不可以被赋值给’a’ | ‘e’,所以返回c
- 最后,never和b,c的联合类型为’b’ | ‘c’
可以实现从类型T中移除不需要的类型,如undefined和null,定义一个NotNull,从T中过滤掉undefined和null
// Diff扩展:从T中过滤掉undefined和null
type NotNull<T> = Diff<T, undefined | null>
// 过滤掉undefined和null,T5的类型就变成了string和number
type T5 = NotNull<string | number | undefined | null>
上述实现过程,在TS中有内置的方法:Extract和Exclude和NonNullable以及ReturnType和InstanceType;
Exclude作用是从类型T中过滤掉可以赋值给类型U的类型
Extract作用是可以从类型T中抽取出可以赋值给U的类型
// Extract<T, u>和Exclude<T, U>
type T6 = Extract<'a' | 'b' | 'c', 'a' | 'e'>
type T7 = Exclude<'a' | 'b' | 'c', 'a' | 'e'>
TS中Extract和Exclude的源码分析
/**
* Exclude from T those types that are assignable to U
*/
type Exclude<T, U> = T extends U ? never : T;
/**
* Extract from T those types that are assignable to U
*/
type Extract<T, U> = T extends U ? T : never;
NonNullable,排除掉所有空的类型
// NonNullable,排除掉所有空的类型
type T9 =NonNullable<string | number | undefined | null>
NonNullable源码
/**
* Exclude null and undefined from T
*/
type NonNullable<T> = T extends null | undefined ? never : T;
ReturnType
// ReturnType<T>可以获取一个函数返回值的类型
type T8 = ReturnType<() => string>
TS中ReturnType源码:
/**
* Obtain the return type of a function type
*/
type ReturnType<T extends (...args: any) => any>
= T extends (...args: any) => infer R ? R : any;
实现分析:
T extends (…args: any) => any:ReturnType要求参数T可以赋值给一个函数,这个函数有任意的参数,返回值类型也是任意的
由于函数返回值类型不确定,这里使用了infer关键字,表示待推断,延迟推断,需要根据实际的情况确定
infer R ? R : any:
如果实际类型是R,那么结果类型就是R,否则返回值类型就是any
InstanceType
class CDemo {
constructor(x:number,y:string){}
}
type CType = InstanceType<typeof CDemo>
TypeScript源码的实现
/**
* Obtain the return type of a constructor function type
*/
type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
对象的空值合并运算符
有时,在访问属性时 TypeScript 会自动帮你插入可选链操作符。类似 let t = myObj?.property
,这样的话变量 t
将被赋值为 property
或 undefined
。
let t = myObj?.text ?? " "
如果myObj下没有属性text则会返回undefined,然而有些情况不希望返回的是undefined,可以用??设置默认值。
?. 可选链操作符
?? 空值合并运算符
数组和元组
数组
const arr:(number | string)[] = [1,'a',2]
const stringArr:string[] = ['a', 'b', 'c']
数组存储对象类型
// type alias 类型别名
type student = {
name:string,
age:number
}
const objArr:student[] = [{
name: 'ts',
age:12
}]
元组 tuple
const teacher:[string, string, number] = ['chinese','grey',21]
接口 interface
出现重复定义的类型判断
const getName = (person: {name:string}): string => {
return person.name;
};
const setName = (person: {name:string}, name: string): void => {
person.name = name;
};
上面代码重复出现name:string的定义。可以定义成接口
interface IPerson {
name: string;
}
const getName = (person: IPerson): string => {
return person.name;
};
const setName = (person: IPerson, name: string): void => {
person.name = name;
};
- 接口中使用?定义可选参数
- 使用readonly定义只读参数 ```typescript interface IPerson { name: string; readonly gender: string; age? : number; }
const getName = (person: IPerson): string => { return person.name; };
<a name="6c8d5cc957d0eae4a6bc228dc913ce01"></a>
### 定义一些新增属性
```typescript
interface IPerson {
name: string;
age: number;
[props: string]: any;
}
const getName = (person: IPerson): string => {
return person.name + person.age;
};
getName({
name: "ts",
age: 1,
other: true,
});
接口定义方法
interface IPerson{
name:string;
say():string;
}
const getName = (person: IPerson): string => {
return person.name;
};
getName({
name:'ts',
say(){
return 'hello ts!'
}
})
类class 实现接口
interface IPerson{
name:string;
say():string;
}
class Student implements IPerson{
public name:string;
say(){
return "我叫:" + this.name;
}
}
接口的继承
interface IPerson{
name:string
}
interface IStudent extends IPerson {
grade: string;
}
const xiaoming: IStudent = {
name: "xiaoming",
grade: "五年级"
};
函数Function
函数的参数类型定义
const add=(x:number, y:number, z?:number): void =>{
if(typeof z=== 'number'){
return x+y+z;
}else{
return x + y
}
}
interface ISum{
(x:number, y:number, z?:number):number;
}
let add1:ISum = add;
可选参数/默认参数
// 使用?表示可选参数,可选参数必须放后边位置
const userName=(firstName:string, lastName?:string):string => {
return firstName + lastName
}
// 默认参数,可以给参数设置一个默认值
const defaultUserName=(firstName:string="shen", lastName?:string):string => {
return firstName + lastName
}
重载
同一个函数赋值不同个数的参数,或者参数的类型不同
function setParams(parasA:string):string{
return parasA.toUpperCase();
}
function setParams(parasA:string,paramsB:number):string{
return parasA.repeat(paramsB)
}
定义类class
- 使用extends继承父类
- 重写父类方法时,使用super可以调用父类中定义的方法 ```typescript class Language { name: string = “ts”; work() { return “writing “ + this.name; } } class Front extends Language { work() { return “前端开发” + super.work(); } } let l1 = new Language(); console.log(l1.work());
let l2 = new Front(); console.log(l2.work());
<a name="96d5f3bf06f8b8fed47f3a1c2a8f6fb2"></a>
### 定义属性时,设置访问类型修饰符
- private 私有
- protected 子类可用
- public 公用
```typescript
class Person {
constructor(public name:string, protected age: number, private password:string){
this.name = name;
this.age = age;
this.password = password;
}
}
readonly只读,不可修改
class Person {
constructor(public readonly name:string){
this.name = name;
}
}
let sam = new Person("sam")
//无法分配到 "name" ,因为它是只读属性
sam.name = "xxx"
constructor构造器
class Man {
// 传统写法
// public name:string;
// constructor(name:string){
// this.name = name;
// }
// 简化写法
constructor(public name: string) {}
}
构造器中参数的继承,super关键字
class Man {
constructor(public name: string) {}
work(n:string){
console.log(n)
}
}
class Boy extends Man {
constructor(public age: number) {
// 可以继承父类的name属性
super("ts");
}
getBoyInfo() {
return `${this.name} + ${this.age}`;
}
work(n:string){
console.log("boy work");
// 可以用super关键字调用父类的方法
super.work(n)
}
}
let b = new Boy(1);
静态属性
getter & setter属性定义的为原型属性
- private定义的私有属性,外部无法访问,可以使用getter和setter解决
- 内部私有属性一般定义为 “_属性”
getter/setter定义时看似为函数,实际调用时是作为属性
class Person {
constructor(private _name: string) {}
get name() {
return this._name + "加密";
}
set name(name: string) {
this._name = name;
}
}
let p = new Person("ts");
console.log(p.name);
p.name = "js";
console.log(p.name);
static静态属性,类本身的属性
属性是通过类直接调用,不用实例化调用
class Instance {
private static _instance: Instance;
// private构造器,不能实例化类
private constructor() {}
static getInstance() {
if (!this._instance) {
this._instance = new Instance();
}
return this._instance;
}
}
// 通过类Instance直接调用getInstance方法
const demo1 = Instance.getInstance();
const demo2 = Instance.getInstance();
console.log(demo1 === demo2);
抽象类abstract
多个类要同时实现一个方法,可以把该方法定义为一个抽象类中的抽象方法
- 抽象类只能被继承,不能被实例化
- 抽象类中的抽象方法,不能写具体的实现
- 继承了抽象类的类,必须实现抽象类中的抽象方法 ```typescript abstract class Gemo { getType() { return “Gemo type.”; } abstract getArea(): number; }
class Circle extends Gemo { constructor(public radius: number) { super(); } getArea() { return this.radius Math.PI; } } class Square extends Gemo { constructor(public width: number) { super(); } getArea() { return this.width this.width; } }
<a name="b3b86024c7a3631f62a16a743ffd4938"></a>
## 泛型
泛型的意义在于函数的重用性,设计原则希望组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型<br />根据业务最初的设计,函数identity入参为string
```javascript
function identity(arg: String){
return arg
}
console.log(identity('100'))
但是随着业务迭代过程,参数需要支持number
function identity(arg: String){
return arg
}
console.log(identity(100)) // Argument of type '100' is not assignable to parameter of type 'String'.
注意尽量避免使用any,any会丢失一些信息,我们无法确定返回值是什么类型。
泛型可以保证入参和返回值是相同类型,泛型是一种特殊的变量,只用于表示类型而不是值。
语法:(arg:T):T 其中T为自定义变量
const hello : string = "Hello vue!"
function say<T>(arg: T): T {
return arg;
}
console.log(say(hello)) // Hello vue!
泛型约束,
使用interface和extends进行类型约束
泛型无法保证每种类型都有某一属性
const hello : string = "Hello vue!"
function say<T>(arg: T): T {
console.log(arg.length) // Property 'length' does not exist on type 'T'.
return arg;
}
console.log(say(hello)) // Hello vue!
要在约束层面上就提示错误,需要定义一个接口来描述约束条件
interface Lengthwise {
length: number;
}
function say<T extends Lengthwise>(arg: T): T {
console.log(arg.length)
return arg;
}
console.log(say(1)) // Argument of type '1' is not assignable to parameter of type 'Lengthwise'.
console.log(say({value: 'hello vue!', length: 10})) // { value: 'hello vue!', length: 10 }
范型变量
范型T可作为变量,传入到args的类型定义中。
function getLength<T>(args:T[]):T[]{
console.log(args.length)
return args;
}
范型类型
function identity<T>(args:T):T{
return args
}
interface setIdentityFn<T>{
(args:T) : T
}
let myIdentity: setIdentityFn<number> = identity
范型约束,约束对象的属性,
使用extends和对象的keyof
function getProperty<T, U extends keyof T>(obj:T, key: U):T[U]{
return obj[key]
}
let test = {a:1,b:2,c:3,d:4}
console.log(getProperty(test,"a"))
// test不包含e属性,调用就会报错
// console.log(getProperty(test,"e"))
类使用范型
定义的类,也可以进行范型约束,约束内部变量data的类型。通过新建类时指定传入的类型。
class Queue<T>{
private data:T[] = []
push(item:T){
return this.data.push(item)
}
pop():T{
return this.data.shift()
}
}
const queue = new Queue<number>()
queue.push(66)
console.log(queue.pop().toFixed())
接口interface的范型约束
interface KeyPair<T, U>{
key : T
value: U
}
let k1: KeyPair<string, number> = {key: "str", value:100}
let k2: KeyPair<number, string> = {key: 66, value: "string"}
高级类型
联合类型
interface JavaInterface {
helloJava(): void;
build(): void;
}
interface JsInterface {
helloJavaScript(): void;
build(): void;
}
class Java implements JavaInterface {
static Strong:boolean = true;
helloJava() {
console.log("Hello Java")
}
build(){
console.log("build Java")
}
}
class JavaScript implements JsInterface {
static Strong:boolean = false;
helloJavaScript() {
console.log("Hello JavaScript")
}
build(){
console.log("build JavaScript")
}
}
// ts类型保护
enum Type { Strong, Week };
function getLanguage(type: Type):JavaInterface|JsInterface {
let lang = type === Type.Strong ? new Java() : new JavaScript();
return lang
}
let j = getLanguage(Type.Strong)
// 只可以调用联合类型
console.log(j.build());
// 下面两个调用都会报错
// console.log(j.helloJava(),j.helloJavaScript());
交叉类型
// 交叉类型 a&b
function combine<T, U>(objA:T, objB:U):T & U{
let result = {} as T & U;
for(let id in objA){
result[id] = objA[id] as any
}
for(let id in objB){
if(!result.hasOwnProperty(id)){
result[id] = objB[id] as any
}
}
return result;
}
class Person{
constructor(public name:string){
this.name = name;
}
}
interface logger{
log(): void;
}
class ConsoleLog implements logger{
log(){
console.log("class ConsoleLog ");
}
}
let sam = combine(new Person("sam"), new ConsoleLog())
console.log(sam.name, sam.log());
类型保护
1.类型断言as
通过as断言,判断对象的类型
interface Bird{
fly(height: number): void;
layEggs(num: number): string;
}
interface Fish{
swim(): boolean;
layEggs(num: number):string;
}
class BirdC implements Bird {
fly(height: number): void{
console.log(`fly ${height}m`);
}
layEggs(num: number):string{
return `lay ${num} eggs`
}
}
class FishC implements Fish {
swim(): boolean{
console.log("can swim");
return true;
}
layEggs(num: number):string{
return `lay ${num} eggs`
}
}
type Pet = FishC | BirdC;
function getPet(p:Pet):Pet{
return p
}
let pet = getPet(new BirdC())
let petFish = getPet(new FishC())
// 这里只能使用共用的属性
console.log(pet.layEggs(6));
// 要想使用不同的属性,可以使用as断言
if((pet as FishC).swim){
(pet as FishC).swim()
}
if((pet as BirdC).fly){
(pet as BirdC).fly(10)
}
if((petFish as FishC).swim){
(petFish as FishC).swim()
}
2.类型谓词 instance is classA
type Pet = FishC | BirdC;
// 可以使用实例is类,判断对象的类型,进行类型保护,然后就可以调用不同类型的方法
function isFish(pet: Fish | Bird): pet is Fish{
return (pet as Fish).swim !== undefined;
}
function getPet(p:Pet):Pet{
if(isFish(p)){
p.swim();
} else {
p.fly(8);
}
return p
}
let pet = getPet(new BirdC())
let petFish = getPet(new FishC())
3:typeof判断基本类型
function getLanguage( x: string | number = "default") {
if(typeof x ==="string"){
x = x.toUpperCase()
}else{
x = x.toFixed(2)
}
return { x }
}
4:instanceof判断对象
type Pet = FishC | BirdC;
function getPet(p:Pet):Pet{
if(p instanceof FishC){
p.swim();
} else {
p.fly(8);
}
return p
}
5:in关键字
type Pet = FishC | BirdC;
function getPet(p:Pet):Pet{
if("swim" in p){
p.swim();
} else {
p.fly(8);
}
return p
}
extends keyof和in keyof的区别
type Partial<T> = {
[P in keyof T]?: T[P];
};
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};