当前位置:网站首页>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
}
边栏推荐
- Uncaught typeerror: cannot red properties of undefined (reading 'beforeeach') solution
- The best way to learn SEO: search engine
- Babbitt | metauniverse daily must read: the group image of Chinese Internet enterprises pouring into metauniverse: "there are only various survival desires, and there is no ambition for forward-lookin
- The psychological process from autojs to ice fox intelligent assistance
- 位运算异或
- 学go之路(二)基本类型及变量、常量
- Seriously recommend several machine learning official account
- Memory error during variable parameter overload
- idea控制台彩色日志
- CDN acceleration and cracking anti-theft chain function
猜你喜欢
作者已死?AI正用艺术征服人类
The first Baidu push plug-in of dream weaving fully automatic collection Optimization SEO collection module
Wechat brain competition answer applet_ Support the flow main belt with the latest question bank file
3. Business and load balancing of high architecture
Missing monitoring: ZABBIX monitors the status of Eureka instance
Seriously recommend several machine learning official account
Detailed explanation | detailed explanation of internal mechanism of industrial robot
Top test sharing: if you want to change careers, you must consider these issues clearly!
开源的网易云音乐API项目都是怎么实现的?
Multi attribute object detection on rare aircraft data sets: experimental process using yolov5
随机推荐
Thought map of data warehouse construction
Supervisor usage document
(4) Web security | penetration testing | network security web site source code and related analysis
leetcode59. 螺旋矩阵 II(中等)
How MySQL merges data
Uni app practical project
C - Inheritance - polymorphism - virtual function member (lower)
Cookie技术&Session技术&ServletContext对象
可变参数重载时的内存错误
Bio model realizes multi person chat
word中把带有某个符号的行全部选中,更改为标题
The best way to learn SEO: search engine
Win10 64 bit Mitsubishi PLC software appears oleaut32 DLL access denied
MVVM of WPF
Simple and understandable high-precision addition in C language
Kubernetes cluster builds ZABBIX monitoring platform
LeetCode 78:子集
[some special grammars about C]
Multi attribute object detection on rare aircraft data sets: experimental process using yolov5
数字IC设计笔试题汇总(一)