代码示例传送门
TypeScript接口的使用
1.声明对象类型
接口和 类型别名类似,都是用来定义对象格式的
// 常规通过类型 type 别名来声明对象类型
type InfoTypeOld = {readonly name: string, age: number}
const inf: InfoTypeOld = {
name: "jj",
age: 20
}
// 建议方法
// 接口 interface 方式声明对象
interface InfoType {
name: string,
readonly age: number
}
const info: InfoType = {
name: "tom",
age: 18
}
2.索引类型
interface接口可以定义索引的类型
// 通过 interface 定义索引类型
// 限定索引是number类型, 值是string类型
interface IndexLanguage {
[index: number]: string
}
const frontLanguage:IndexLanguage = {
0: "HTML",
1: "CSS",
2: "JavaScript",
// "X": "VueJs"
}
// 限定索引是 string, 值 是number
interface languageBirthday {
[index: string]: number
}
const languageYear: languageBirthday = {
"C": 1972,
"Java": 1995,
"JavaScript": 1996,
"TypeScript": 2014
}
3.函数类型
定义接口内属性为函数类型
// type CalcFn = (n1: number, n2: number) => number
interface CalcFn {
calc: (n1: number, n2: number) => number
}
function calc(num1: number, num2: number, calcFn: CalcFn) {
return calcFn.calc(num1, num2)
}
const add: CalcFn = {
calc: (num1, num2) => num1 + num2
}
const result = calc(20, 30, add)
console.log(result);
4.接口的继承
接口也能够继承,不过接口的继承可以继承多个,类的继承只能继承一个
接口继承后,能够合并接口的属性
interface Parent1 {
beard: boolean
}
interface Parent2 {
color: string
}
interface Person extends Parent1,Parent2 {
from: string
}
const p: Person = {
beard: true,
color: "yellow",
from: "China"
}
console.log(p);
5.交叉类型
联合类型标识符是 | :表示既可以..也可以...
交叉类型标识符是 &:表示都要
// 联合类型
type WhyType = number | string
type Direction = "left" | "right"
// 交叉类型
type WDType = number & string
interface ISwim {
swimming: () => void
}
interface IFly {
flying: () => void
}
type MyType1 = ISwim | IFly
type MyType2 = ISwim & IFly
const obj1:MyType1 = {
flying() {}
}
const obj2:MyType2 = {
flying() {},
swimming() {}
}
6.接口的实现
接口中的方法类似于抽象类中的方法,属性只需要写类型,当实现的时候必须都要实现函数体。
接口的实现可以一次实现多个接口
interface IEat {
eating: () => void
}
interface ISwim {
swimming: () => void
}
// 类实现接口
class Animal {
type: string = ""
}
// 继承:只能继承一个类
// 实现:实现接口,可以实现多个接口
class Fish extends Animal implements ISwim, IEat{
constructor(type: string = "草鱼") {
super()
this.type = type
}
eating() {
console.log("fish eating");
}
swimming() {
console.log("fish swimming");
}
}
const fish = new Fish()
// 面向接口编程
// 参数:只要是实现了ISwim接口的类都能够传入函数
function swim(iSwim: ISwim) {
iSwim.swimming()
}
swim(fish)
7. 接口interface和别名type的区别
区别: 当实现了同名的接口时,接口内会进行合并处理;而类型别名不允许存在同名的别名。
interface IFoo {
name: string
}
interface IFoo {
age: number
}
// 同名的接口其中的属性会做合并处理
// 因此IFoo类型,必须存在两个属性
// 可以用来给全局的接口添加属性
const foo: IFoo = {
name: "tom",
age: 18
}
type IGo = {
name: string
}
// type 类型别名不能同名
// type IGo = {}
8.字面量赋值
给变量初始化时,可以传入一个和包含指定类型属性的变量
interface IPerson {
name: string,
age: number
}
const info = {
name: "tom",
age: 18,
height: 1.88
}
// 直接赋值info的内部属性,则多余height,不允许
// 当使用info对象赋值时,typescript会进行相关处理
// 处理:擦出掉多余的依然符合IPerson的话,不会报错
const p: IPerson = info
console.log(p);
TypeScript枚举类型
枚举可以使用下标,也可以使用属性赋值;默认是0下标开始,也可以自定义。
// 枚举常量一般使用大写,代码习惯
enum Direction {
LEFT,
RIGHT,
CENTER,
TOP = 100
}
const d:Direction = Direction.TOP
console.log(d); // 100
function turnDirection(direction: Direction) {
console.log(direction); // 值是默认下标,也可以自定义
switch(direction) {
case Direction.LEFT:
console.log("左转");
break;
case Direction.RIGHT:
console.log("右转");
break;
default:
console.log("起飞");
}
}
// 两种传参方式,传的参都是一致的
turnDirection(Direction.LEFT)
turnDirection(0)
TypeScript中的this(ThisType)
1. 对象内调用
方法作为对象的属性时,其内部this会被自动推导
const obj1 = {
name: "name",
getNam() {
return this.name // 可以自动推导为{ name:string, getName():string}类型
},
}
// 使用箭头函数会报错
// 箭头函数没有自己的this
const obj2 = {
name: "name",
getNam: () => {
return this.name // error: The containing arrow function captures the global value of 'this'
},
}
2. 构造函数
使用ES5语法的函数定义类,需要推导出构造函数的类型。
推荐使用ES6语法 class来写类
function People(name: string) {
this.name = name // error: this是any类型,无法推导出正确的类型
}
new People("223") // error: 'new' expression, whose target lacks a construct signature, implicitly has an 'any' type.
3. 显式声明this的类型
通过call,apply调用,传入指定的this类型
const person = {
name: "name",
getNam() {
return this.name
},
}
function setName(this: typeof person, name: string) {
this.name = name
}
setName.call(person, 'new name')
TypeScript泛型的使用
1.认识泛型
示例:实现一个两个相同参数相加
// 不同的参数让参数过于冗长
function sum(num1: number|string|any[], num2: number|string|any[]) {
// return num1 + num2
}
使用泛型,使参数动态化;定义函数不定义参数类型,而是让传入的参数决定;Type 是自己定义的,这个可以自定义(默认使用T)
function sum<Type>(num1: Type): Type {
return num1
}
// 1. 明确的传入类型
sum<number>(20)
sum<{name:string}>({name: "hello"})
sum<any[]>(["hah"])
// 2. 类型推导
sum(50)
sum("world")
2.泛型接收参数类型
泛型让参数根据传入是定义,而不是限定死;
注意: 同一种泛型变量意味着是同一种类型,不同的才是不同的类型
function foo<T, E, O>(arg1: T, arg2:E, arg3: O) {
}
foo<number, string, boolean>(10, "30", true)
foo(true, "3", [""])
3.泛型接口
接口也能够定义属性类型
interface IPerson<T, E> {
name: T,
age: E
}
const p: IPerson<string, number> = {
name: "tom",
age: 18
}
4.泛型类
设置同一个类型,则传输必须为相同类型
class Person<T> {
name: T
age: T
isMan: T
constructor(name: T, age: T, isMan: T) {
this.name = name
this.age = age
this.isMan = isMan
}
}
const p1 = new Person("tom", "18 years old", "true")
const p2 = new Person<string>("tom", "18 years old", "true")
const p3: Person<string> = new Person("tom", "18 years old", "true")
5.泛型的类型约束
泛型也能够继承,泛型就是正常类型的使用方式
interface ILength{
length: number
}
// T 继承接口 ILength ;因此参数必需拥有length属性
function getLength<T extends ILength>(arg: T) {
return arg.length
}
getLength("abc") // 字符串拥有长度
getLength(["abc"]) // 数组拥有长度
getLength({length: 100}) // 拥有length属性的对象也可以
// getLength(100) // 数字就不行了
TypeScript的模块化开发
TypeScript代码都在同一个作用域内,因此变量的命名不能重复
那如何解决该方法呢?
有两种方式:模块化 和 使用命名空间
模块化
不同的文件,在最后面导出个对象,就意味着该文件是一个独立的模块对象,这样就拥有自己的作用域。
function sum (a: number, b: number) {
return a + b
}
function sub (a: number, b: number) {
return a - b
}
export {
sum,
sub
}
命名空间
关键字 namespace: 命名空间,让函数重名不再有问题
// 内部模块
export namespace time {
export function format(time: string) {
return "2022-1-23"
}
}
time.format("x")
export namespace price {
export function format(price: string) {
return "$9.15"
}
}
price.format("x")
自定义全局变量和第三方插件
有时候需要自定义一个全局变量或者引入第三方插件,TypeScript无法识别;这是因为TypeScript在内部有声明变量或者模块的文件(以.d.ts结尾的),在文件内部声明想要声明的模块或者变量等。
// 手写声明模块
declare module 'lodash' {
export function join(arr: any[]) {}
}
// 声明变量、类、函数
declare let myName: string = "typescript"
declare function foo(): void
declare class Person {
name: string
age: number
constructor(name: string, age: number)
}
// 声明文件
declare module "*.jpg"
TypeScript的模块化开发
TypeScript代码都在同一个作用域内,因此变量的命名不能重复
那如何解决该方法呢?
有两种方式:模块化 和 使用命名空间
模块化
不同的文件,在最后面导出个对象,就意味着该文件是一个独立的模块对象,这样就拥有自己的作用域。
function sum (a: number, b: number) {
return a + b
}
function sub (a: number, b: number) {
return a - b
}
export {
sum,
sub
}
命名空间
关键字 namespace: 命名空间,让函数重名不再有问题
// 内部模块
export namespace time {
export function format(time: string) {
return "2022-1-23"
}
}
time.format("x")
export namespace price {
export function format(price: string) {
return "$9.15"
}
}
price.format("x")
自定义全局变量和第三方插件
有时候需要自定义一个全局变量或者引入第三方插件,TypeScript无法识别;这是因为TypeScript在内部有声明变量或者模块的文件(以.d.ts结尾的),在文件内部声明想要声明的模块或者变量等。
// 手写声明模块
declare module 'lodash' {
export function join(arr: any[]) {}
}
// 声明变量、类、函数
declare let myName: string = "typescript"
declare function foo(): void
declare class Person {
name: string
age: number
constructor(name: string, age: number)
}
// 声明文件
declare module "*.jpg"