当前位置:网站首页>TypeScript接口与泛型的使用
TypeScript接口与泛型的使用
2022-07-06 07:18:00 【wendyTan10】
TypeScript接口与泛型的使用
(一)TypeScript接口
1.声明一个对象类型
// 通过类型(type)别名声明
type InfoType = {
name: string, age: number}
接口interface
,并且可以定义可选类型(?
),也可以定义只读属性(readonly
)
interface IInfoType {
readonly name: string
age: number
friend?: {
name: string
}
}
const info: IInfoType = {
name: "why",
age: 18,
friend: {
name: "kobe"
}
}
console.log(info.friend?.name)
console.log(info.name)
info.age = 20
2.索引类型
当我们的对象的key,value不确定的时候,我们可以:
// 通过interface来定义索引类型
interface IndexLanguage {
[index: number]: string
}
const frontLanguage: IndexLanguage = {
0: "HTML",
1: "CSS",
2: "JavaScript",
3: "Vue"
}
interface ILanguageYear {
[name: string]: number
}
const languageYear: ILanguageYear = {
"C": 1972,
"Java": 1995,
"JavaScript": 1996,
"TypeScript": 2014
}
3.函数的类型
函数的类型也可通过interface
的方式定义,但是一般都建议使用type
的方式进行定义;
// type CalcFn = (n1: number, n2: number) => number
// 可调用的接口
interface CalcFn {
(n1: number, n2: number): number
}
function calc(num1: number, num2: number, calcFn: CalcFn) {
return calcFn(num1, num2)
}
const add: CalcFn = (num1, num2) => {
return num1 + num2
}
calc(20, 30, add)
4.接口的继承
接口interface
的继承与类class类似,都是使用关键字extends
,并且接口可实现多继承(类不支持)
interface ISwim {
swimming: () => void
}
interface IFly {
flying: () => void
}
interface IAction extends ISwim, IFly {
}
const action: IAction = {
swimming() {
},
flying() {
}
}
5.交叉类型
继联合类型的使用:
// 联合类型
type WhyType = number | string
type Direction = "left" | "right" | "center"
另外一种类型的方法叫交叉类型
(Intersection Types):
type WType = number & string;
但不存在一个变量即满足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 = {
swimming() {
},
flying() {
}
}
6.接口的实现
interface ISwim {
swimming: () => void
}
interface IEat {
eating: () => void
}
// 类实现接口
class Animal {
}
// 继承: 只能实现单继承
// 实现: 实现接口, 类可以实现多个接口
class Fish extends Animal implements ISwim, IEat {
swimming() {
console.log("Fish Swmming")
}
eating() {
console.log("Fish Eating")
}
}
class Person implements ISwim {
swimming() {
console.log("Person Swimming")
}
}
// 编写一些公共的API: 面向接口编程
function swimAction(swimable: ISwim) {
swimable.swimming()
}
// 1.所有实现了接口的类对应的对象, 都是可以传入
swimAction(new Fish())
swimAction(new Person())
swimAction({
swimming: function() {
}})
interface和type区别
我们时常会困惑interface
和type
用来定义对象类型有什么不同,该怎么去选择?
- 定义非对象类型,通常推荐使用
type
,比如Direction、Alignment、一些Function; - 定义对象类型,那么他们是有区别的:
interface
可以重复的对某个接口来定义属性和方法;type
定义的是别名,别名是不能重复的;
7.字面量的赋值(freshness擦除)
将一个变量标识符赋值给其他的变量时,会进行freshness
擦除操作:
interface IPerson {
name: string
age: number
height: number
}
function printInfo(person: IPerson) {
console.log(person);
}
// 代码在编译的时候就会直接的报错,不存在address属性
printInfo({
name: "why",
age: 18,
height: 1.88,
address: "广州市",
})
类型检测不通过
而这里只使用自己定义好的值,多余的值会进行freshness擦除后进行类型检测,并且通过ts的类型检测,
// ts的检测会自动的推倒出一个info的字面量类型
// 并且具备有address的属性,将其赋值到printInfo()中就会将address擦除掉;
const info = {
name: "wendy",
age: 18,
height: 1.88,
address: "深圳市"
}
// 赋值的是对象的引用,会进行属性的擦除
printInfo(info);
// {name:'wendy',age: 18, height: 1.88, address: '深圳市'}
// 并且是无法取出address的值
8.枚举类型
枚举是将一组可能出现的值,一个个列举出来,定义在一个类型中,这个类型就是枚举类型;
枚举类型的使用:允许开发者定义一组命名常量,常量可以是数字number
、字符串类型string
;如下:
// type Direction = "left" | "Right" | "Top" | "Bottom"
// 枚举的类型一般都是大写,字符串的常量
enum Direction {
LEFT,
RIGHT,
TOP,
BOTTOM
}
// 枚举类似与一个数字的常量,等同于:
// enum Direction {
// LEFT = 0,
// RIGHT = 1,
// TOP = 2,
// BOTTOM =3
// }
function turnDirection(direction: Direction) {
switch (direction) {
case Direction.LEFT:
console.log("改变角色的方向向左")
break;
case Direction.RIGHT:
console.log("改变角色的方向向右")
break;
case Direction.TOP:
console.log("改变角色的方向向上")
break;
case Direction.BOTTOM:
console.log("改变角色的方向向下")
break;
default:
const foo: never = direction;
break;
}
}
turnDirection(Direction.LEFT)
turnDirection(Direction.RIGHT)
turnDirection(Direction.TOP)
turnDirection(Direction.BOTTOM)
枚举类型默认是有值的,比如上面的枚举,默认值是这样的:
enum Direction {
LEFT = "LEFT",
RIGHT = "RIGHT",
TOP = "TOP",
BOTTOM = "BOTTOM"
}
当然,我们也可以给枚举其他值,比如这个时候会从100进行递增;
enum Direction {
LEFT = 100,
RIGHT,
TOP,
BOTTOM
}
(二)TypeScript泛型的使用
代码的构建不仅仅是规范与严谨性,还希望代码具有复用性,就好比我们封装一些API时,通过传入不同参数执行不同的事件,但对于参数的类型是否也可以参数化
;这个就叫做类型的参数化
;
1.泛型的基本使用
类似于:封装一个函数,传入一个参数,并返回这个参数;
// 返回的数据的类型是一致
function fun(mes: string):string {
return mes;
}
上面的代码虽然实现返回的类型一致,但是却无法适用于其他的类型,只是在此固定为string
的类型;
// any的类型即将丢失类型的信息,与最先无定义的无差别
function fun(mes: any):any {
return mes;
}
我们在这里使用特殊的变量-类型变量(type variable),它作用于类型,而不是值;
// 在定义这个函数时, 我不决定这些参数的类型
// 而是让调用者以参数的形式告知,我这里的函数参数应该是什么类型
function sum<Type>(num: Type): Type {
return num
}
这里我们可以使用两种方式来调用它:
- 方式一:通过 <类型> 的方式将类型传递给函数;
// 明确的传入类型 sum<number>(20) sum<{ name: string}>({ name: "why"}) sum<any[]>(["abc"])
- 方式二:通过类型推到,自动推到出我们传入变量的类型:
// 调用方式二: 类型推导 sum(50); sum("abc");
2.泛型可传入多个参数
function foo<T, E, O>(arg1: T, arg2: E, arg3?: O, ...args: T[]) {
}
foo<number, string, boolean>(10, "abc", true)
并且我们在平常的开发当前,经常使用到名称缩写:
- T: Type缩写;
- K,V: key和value的缩写,键值对;
- E: Element的缩写,元素;
- O: Object的缩写,对象;
3.泛型的接口使用
interface IPerson<T> {
id: T
numList: T[],
getID:( vallue: T) => void;
}
const p: IPerson<number> = {
id: 1,
numList: [99, 10, 10],
getID: function(id: number) {
console.log(id)
}
}
4.泛型的类使用
定义一个泛型类的使用:
class Point<T> {
x: T
y: T
z: T
constructor(x: T, y: T, z: T) {
this.x = x
this.y = y
this.z = y
}
}
const p1 = new Point("1.33.2", "2.22.3", "4.22.1")
const p2 = new Point<string>("1.33.2", "2.22.3", "4.22.1")
const p3: Point<string> = new Point("1.33.2", "2.22.3", "4.22.1")
5.泛型的约束
我们有个需求是希望传入的类型有某些共性,但共性可不在同一类型当中:
就好比我们希望传入的类型都有length
的属性,所以该类型可能是string
,array
或某些对象
;那如果这里我们要求只要是具备length
的属性的都可作为参数类型,这个应该如何操作:
interface ILength {
length: number
}
function getLength<T extends ILength>(arg: T) {
return arg.length
}
getLength("abc")
getLength(["abc", "cba"])
getLength({
length: 100})
(三)TypeScript的作用域
TypeScript
支持两种方式来控制我们的作用域:
- 模块化:每个文件可以是一个独立的模块,支持
ES Module
,也支持CommonJS
;export function add(num1: number, num2: number) { return num1 + num2; } export function sub(num1: number, num2: number) { return num1 - num2; }
- 命名空间:通过
namespace
来声明一个命名空间
早期时被称为"内部模块",主要将模块的内部进行作用域的划分,防止一些命名的冲突问题;export namespace Time { export function format(time: string) { return '2022-07-05'; } } // 同个方法名称不同的命名空间中定义 export namespace Price { export function format(time: string) { return '20.22'; } } // 引入之后调用 console.log(time.format("11111111")); console.log(price.format(123));
(四)类型的查找与声明
项目中的类型,几乎都是我们自己编写的,但是也有一些其它的类型:
const imageEl = document.getElementById("image") as HTMLAnchorElement;
都会很好奇,这里的HTMLAnchorElement
的类型来自哪里?
这里涉及到typescript
对类型的管理
和查找
规则:
有关typescript
的文件: .d.ts
文件;用于做类型的声明(declare
),仅仅用来做类型的检测,告知typescript
我们拥有哪些类型;
而typescript
会在哪里查找我们的类型声明呢?
- 内置类型声明
- 外部定义类型声明
- 自己定义类型声明
1.内置类型声明
内置类型声明是typescript
自带的、帮助我们内置了JavaScript
运行时的一些标准化API
的声明文件; 包括Math
、Date
等内置类型,也包括DOM
API
(Window、Document);
const imageEl = document.getElementById("image") as HTMLAnchorElement;
如这里的getElementById
的属性,在项目的配置文件中是可以查找到;
内置类型声明通常在我们安装typescript
的环境中会带有的;
点击项目文件查看lib相关的.d.ts文件:https://github.com/microsoft/TypeScript/tree/main/lib
2.外部定义类型声明
外部类型声明通常是我们使用一些库(比如导入第三方库),需要额外的添加类型的声明去使用;
这些库通常有俩种类型声明的方式:
方式一:在自己的库中自带的类型声明;导入的第三方库在node_modules
文件中有自己的(.d.ts文件/或可添加);比如axios;
方式二:通过社区公有库DefinitelyTyped
存放类型声明文件
1. 该库的GitHub地址:https://github.com/DefinitelyTyped/DefinitelyTyped/;社区中有大量的已编译好的.d.ts文件可供使用
2. 该库查找声明安装方式的地址:https://www.typescriptlang.org/dt/search?search= ;该地址用于查询项目中所引用的第三方包导入的dt文件
,可查看额外的指令去导入,省去翻阅安装包的目的; 比如我们输入react
;安装react的类型声明: npm i @types/react --save-dev
;
3.自定义声明
当在第三方库中没有声明的文件,并且我们想给自己的代码声明一些类型时,就可自定义声明文件;那么怎么自定义声明文件呢?项目的根目录下创建一个任意文件名.d.ts
文件,并进行编译需要声明的类型:
声明变量/函数/类
declare let whyName: string
declare let whyAge: number
declare let whyHeight: number
declare function whyFoo(): void
declare class Person {
name: string
age: number
constructor(name: string, age: number)
}
我们也可以声明模块,比如lodash
模块默认不能使用的情况,可以自己来声明这个模块:
声明模块
声明模块的语法: declare module '模块名' {}
。
在声明模块的内部,我们可以通过 export
导出对应库的类
、函数
等;
// 声明模块 - lodash是模块的名称
declare module 'lodash' {
export function join(arr: any[]): void
}
声明文件:
在开发vue
的过程中,默认是不识别我们的.vue
文件的,那么我们就需要对其进行文件的声明;
在开发中我们使用了 jpg
这类图片文件,默认typescript
也是不支持的,也需要对其进行声明;
// .vue文件的声明
declare module '*.vue' {
import {
DefineComponent } from 'vue';
const Component: DefineComponent<{
}, {
}, any>;
export default Component;
}
// 声明文件
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.png'
declare module '*.svg'
declare module '*.gif'
声明命名空间
比如我们在index.html
中直接引入了jQuery
:
CDN地址: https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js
"></script>
如果在.ts文件中直接使用就会导致运行报错:
TS2581: Cannot find name ‘$’. Do you need to install type definitions> for jQuery? Try
npm i --save-dev @types/jquery
.
而解决方案有俩种:
- 方式一:安装
@types/jquery
:npm i --save-dev @types/jquery
、 - 方式二:是添加
$
的命名空间:
// 声明命名空间
declare namespace $ {
export function ajax(settings: any): any
}
边栏推荐
- 微信公众号无限回调授权系统源码 全网首发
- supervisor 使用文档
- Kubernetes cluster builds ZABBIX monitoring platform
- You deserve this high-value open-source third-party Netease cloud music player
- win10 64位装三菱PLC软件出现oleaut32.dll拒绝访问
- Bio model realizes multi person chat
- Cif10 actual combat (resnet18)
- The differences and advantages and disadvantages between cookies, seeion and token
- Solution to the problem of breakthrough in OWASP juice shop shooting range
- L'auteur est mort? Ai utilise l'art pour conquérir l'humanité
猜你喜欢
Hydra common commands
The author is dead? AI is conquering mankind with art
Oracle数据库11gr2使用tde透明数据加密报错ora28353,如果运行关闭wallet会报错ora28365,运行打开wallet就报错ora28353无法打开wallet
The ECU of 21 Audi q5l 45tfsi brushes is upgraded to master special adjustment, and the horsepower is safely and stably increased to 305 horsepower
The best way to learn SEO: search engine
You deserve this high-value open-source third-party Netease cloud music player
Oracle database 11gr2 uses TDE transparent data encryption to report an error ora28353. If you run to close the wallet, you will report an error ora28365. If you run to open the wallet, you will repor
First knowledge of OpenGL es learning (1)
这个高颜值的开源第三方网易云音乐播放器你值得拥有
杰理之BLE【篇】
随机推荐
JDBC学习笔记
Path analysis model
chrome查看页面fps
【JDBC】快速入门教程
Uni app practical project
Is software testing outsourcing going or not? Three years' real outsourcing experience tells you
巴比特 | 元宇宙每日必读:中国互联网企业涌入元宇宙的群像:“只有各种求生欲,没有前瞻创新的雄心”...
OpenJudge NOI 2.1 1661:Bomb Game
win10 64位装三菱PLC软件出现oleaut32.dll拒绝访问
Applied stochastic process 01: basic concepts of stochastic process
LeetCode 78:子集
word删除括号里内容
Markdown 中设置图片图注
可变参数重载时的内存错误
supervisor 使用文档
配置树莓派接入网络
多线程和并发编程(二)
CDN acceleration and cracking anti-theft chain function
C语言 简单易懂的高精度加法
leetcode704. Binary search (find an element, simple, different writing)