// 通过类型(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
当我们的对象的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
}
函数的类型也可通过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)
接口interface的继承与类class类似,都是使用关键字extends ,并且接口可实现多继承(类不支持)
interface ISwim {
swimming: () => void
}
interface IFly {
flying: () => void
}
interface IAction extends ISwim, IFly {
}
const action: IAction = {
swimming() {
},
flying() {
}
}
继联合类型的使用:
// 联合类型
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() {
}
}
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用来定义对象类型有什么不同,该怎么去选择?
type,比如Direction、Alignment、一些Function;interface 可以重复的对某个接口来定义属性和方法;type定义的是别名,别名是不能重复的;
将一个变量标识符赋值给其他的变量时,会进行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的值
枚举是将一组可能出现的值,一个个列举出来,定义在一个类型中,这个类型就是枚举类型;
枚举类型的使用:允许开发者定义一组命名常量,常量可以是数字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
}
代码的构建不仅仅是规范与严谨性,还希望代码具有复用性,就好比我们封装一些API时,通过传入不同参数执行不同的事件,但对于参数的类型是否也可以参数化;这个就叫做类型的参数化;
类似于:封装一个函数,传入一个参数,并返回这个参数;
// 返回的数据的类型是一致
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");
function foo<T, E, O>(arg1: T, arg2: E, arg3?: O, ...args: T[]) {
}
foo<number, string, boolean>(10, "abc", true)
并且我们在平常的开发当前,经常使用到名称缩写:
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)
}
}
定义一个泛型类的使用:
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")
我们有个需求是希望传入的类型有某些共性,但共性可不在同一类型当中:
就好比我们希望传入的类型都有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支持两种方式来控制我们的作用域:
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会在哪里查找我们的类型声明呢?
内置类型声明是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
外部类型声明通常是我们使用一些库(比如导入第三方库),需要额外的添加类型的声明去使用;
这些库通常有俩种类型声明的方式:
方式一:在自己的库中自带的类型声明;导入的第三方库在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;

当在第三方库中没有声明的文件,并且我们想给自己的代码声明一些类型时,就可自定义声明文件;那么怎么自定义声明文件呢?项目的根目录下创建一个任意文件名.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
}