🌟TypeScript 介绍
TypeScript 是什么?
TypeScript 是 JavaScript 的超集。
TypeScript = Type + JavaScript(在JS基础之上,为JS添加了类型支持)。
TypeScript为什么要为JS添加类型支持?
对于JS 来说:需要等到代码真正去执行的时候才能发现错误(晚)。
对于TS来说:在代码编译的时候(代码执行前)就可以发现错误(早)。
并且,配合 VSCode 等开发工具,TS 可以提前到在编写代码的同时就发现代码中的错误,减少找Bug、改Bug 时间。
TypeScript相比JS的优势
- 更早(写代码的同时)发现错误,减少找Bug、改Bug时间,提升开发效率。
- 程序中任何位置的代码都有代码提示,随时随地的安全感,增强了开发体验。
- 强大的类型系统提升了代码的可维护性,使得重构代码更加容易。
- 支持最新的ECMAScript语法,优先体验最新的语法,让你走在前端技术的最前沿。
- TS类型推断机制,不需要在代码中的每个地方都显示标注类型,让你在享受优势的同时,尽量降低了成本。
安装TypeScript
npm i -g typescript
查看TypeScript版本
tsc -v
运行ts文件
tsc 文件名.ts
node 文件名.js
简化运行ts
安装
npm i -g ts-node
使用
ts-node 文件名.ts
类型声明
//定义变量
let 变量名:类型
//定义方法
function fn(变量名:类型){
console.log(变量名)
}
//调用
fn();
🌟TypeScript 常用基础类型
JavaScript 原有的类型
js类型 | 关键字 | 数据类型 | 示例 |
---|---|---|---|
原始 | number | 数字类型 | let age: number = 7; |
原始 | string | 字符串类型 | let color: string = "blue"; |
原始 | boolean | 布尔类型 | let isDone: boolean = true; |
原始 | null | null | let n: null = null; |
原始 | undefined | undefined | let u: undefined = undefined; |
原始 | symbol | 独特的不可变值,通常用于对象属性标识符 | let sym: symbol = Symbol(); |
对象 | array | 数组类型 | // 在元素类型后面加上[] let arr: number[] = [1, 2]; // 或者使用数组泛型 let arr: Array<number> = [1, 2]; |
对象 | object | 对象类型 | let obj: object = {name: "John", age: 30}; |
对象 | function | 函数类型 | const add = (x: number, y: number):number => {return x+y}; |
TypeScript 新增的类型
关键字 | 类型 | 示例 |
---|---|---|
union | ~联合类型,可以是多个类型中的一个 | `let id: number |
type | ~类型别名,用于自定义类型(可以用映射类型) | type Point = { x: number, y: number }; let obj1: Point = {x: 3, y: 5}; `type arr = (number |
tuple | 元组类型,表示已知元素数量和类型的数组 | let x: [string, number] = ["hello", 10]; |
enum | 枚举类型,用于定义具有标识符的数值 | enum Color {Red, Green, Blue}; let c: Color = Color.Green; |
any | 任意类型,可以赋值为任意类型的变量 | let notSure: any = 4; notSure = "maybe a string"; |
void | 没有返回值的函数的返回类型 | function warnUser(): void { alert("This is my warning"); } |
literal | 字面量类型,特定值的类型 | `let direction: “left” |
interface | 接口类型,定义对象的结构和行为(不可以用映射类型) | interface Person { name: string; age: number; } |
不常用的类型
类型 | 说明 | 示例 |
---|---|---|
unknown | 未知类型,类型安全的 any ,有类型检查 | let value: unknown = 4; if (typeof value === "number") {} |
never | never 是其它类型(包括 null 和 undefined)的子类型,代表永远不会发生的类型,通常用于不会有返回值的函数 | function error(message: string): never { throw new Error(message); } |
intersection | 交叉类型&,合并多个类型为一个类型 ( 类似于接口继承,常用于对象类型 ) 即都要满足 | type Person = { name: string } & { age: number }; |
readonly | 只读属性,不能被修改 | interface Person { readonly name: string; } |
mapped types | 基于泛型的工具类型 ( 基于已有类型创建新的类型 ) | type Partial<T> = { [P in keyof T]?: T[P]; } |
conditional types | 条件类型,根据条件返回不同类型 | type IsString<T> = T extends string ? true : false; |
联合类型
语法:let 变量名:声明类型1|声明类型2.....;
// 值为 number 或 string 类型
let id: number | string;
// 值为 给定的几个值
let id: 1,5,9,13;
// 数组为 number 数组 或 string 数组
let arr: number | string[];
// 数组的值为 number 或 string 类型
let arr: (number | string)[];
类型别名
语法:type 变量名1 = 声明的复杂长类型;
let 变量名2: 变量名1 = 值;
type arr = (number | string)[];
let arr1:arr=[1,'s'];
let arr2:arr=[1,2,3,'s'];
函数类型
函数的定义
//定义方法
function r1(name){
console.log(name);
}
r1('sdja');
function r2(age:number,name:string){
console.log(age,name);
}
r2(19,'adj1');
//方法返回值
function r3(){
}
var run3=r3();//undefined
console.log("78:"+run3);
function k1():number{
return 19;
}
function k2():string{
return "你好"
}
//返回void
function greeter():void{
console.log("Hello World");
return undefined;//可以
return ;//可以
//其他的返回不可以
//能打印出来Hello World
}
//没有任何返回值 立刻结束
function k4():never{
throw new Error("错误!");
}
//单独指定参数、返回值类型
function add1 (num1:number, num2:number):number{
return numi+num2
}
const add2 = (num1:number, num2:number):number => {
return numi+num2
}
//同时指定参数、返回值类型
const add3:(num1:number, num2:number) => number = (num1, num2) => {
return num1+num2
}
函数参数
//没有返回值
function test():void{
console.log('没有返回值')
}
//有返回值
function test2():number{
let k=1+1;
console.log('有返回值')
return k;
}
//带参数
function test3(a:number,b:number):number{
let o=a+b;
return o;
}
let sum=test3(5,5);
console.log(sum);
//可选参数 (变量名?: 数据类型)
//可选参数必须跟在必需参数后面。 如果上例我们想让 firstName 是可选的,lastName 必选,那么就要调整它们的位置,把 firstName 放在后面。
//如果都是可选参数就没关系。
function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
function myslice(start?: number, end?: number) {
console.log('开始索引'+start+'结束索引'+end);
}
myslice(); //开始索引undefined结束索引undefined
myslice(1); //开始索引1结束索引undefined
myslice(1,3); //开始索引1结束索引3
//给参数传默认值 (变量名:数据类型[] = 默认值)
function sum(x:number, y:number = 20) {
return 2x + y;
}
let sum1 = sum(20,5);
let sum2 = sum(12);
//剩余参数 (...变量名:数据类型[])
function addNumbers(...nums:number[]) {
var i;
var sum:number = 0;
for(i = 0;i<nums.length;i++) {
sum = sum + nums[i];
}
console.log("和为:",sum)
}
addNumbers(1,2,3)
addNumbers(10,10,10,10,10)
//匿名函数
//匿名函数是一个没有函数名的函数。
//匿名函数在程序运行时动态声明,除了没有函数名外,其他的与标准函数一样。
//我们可以将匿名函数赋值给一个变量,这种表达式就成为函数表达式。
let Anonymous = function (){
console.log('匿名函数!');
}
Anonymous();
//匿名带参数
let Anonymous2 = function (a:number, b:number){
console.log(2a-2b);
}
Anonymous(8,7);
//箭头函数
//函数只有一行语句
var foo = (x:number)=>10 + x
//函数是一个语句块
var foo2 = (x:number)=> {
x = 10 + x
console.log(x)
}
console.log(foo(100))
console.log(foo2(100))
//函数立刻运行
(function autorun():void{
console.log('运行函数!');
}());
对象类型
声明对象
// 声明对象-先声明后赋值
let y1:object;
y1={};
y1=function (){
}
// 声明对象-声明后立即赋值
// 多个方法或属性,在一行写时用 分号 隔开
let obj:{ name: string; age:number; say():void; greet(name:string):void } = {
name: '张三',
age: 18,
say() {},
greet(name:string) {
console.log('你好', name);
}
};
// 多行写可省略分号
// 方法的类型也可以使用 箭头函数 `sayHi:()=>void`
let y2:{
name:string
//sayHi():void
sayHi:()=>void
sayHello(name:string):void
}={
name:'李四',
sayHi() {},
sayHello(name) {}
};
对象参数
//可选属性
let y3:{
name:string,
//可有可无
age?:number,
sex?:string,
}
y3={
name:'李四',
age:19,
}
let y4:{
name:string,
//任意字符串 任意类型
[propName:string]:unknown,
}
y4={
name:'李四',
age:19,
}
接口类型interface a
interface User {
name: string,
number: number,
call(): void
}
let user1: User = {
name: '张三',
number: 15522223333,
call() {
console.log('call');
}
}
接口继承 extends
interface User {
name: string,
number: number,
call(): void
}
interface bigUser extends User {
age: number,
watch(): void,
sayHi: ()=>string
}
let user2: bigUser = {
name: '李四',
number: 18899996666,
age: 25,
call() {
console.log('call');
},
watch() {
console.log('watch');
},
sayHi: ():string =>{return "Hi there"}
}
user2.call();
user2.watch();
user2.sayHi();
元组类型[]
// 允许在数组中存储不同类型的元素, 组中的每个元素都有明确的类型和位置。
// 元组可以在很多场景下用于表示 固定长度、且 各元素类型已知 的数据结构
let position: [number, number] = [123,59]
let mytuple: [number, string];
mytuple = [42,"Runoob"];
类型断言as
// 类型断言是一种告诉编译器变量的类型的方式
// 技巧:选择html元素,在浏览器控制台,通过 console.log(dir($0)) 打印DOM元素,在属性列表的最后面,即可看到该元素的类型。
const alink = document.querySelector('a') as HTMLAnchorElement;
// const alink = <HTMLAnchorElement>document.querySelector('a'); // react中不能用这种方式
let someValue: any = "hello world";
// 类型断言告诉编译器 `someValue` 是一个字符串
let strLength: number = (someValue as string).length;
console.log(strLength); // 输出: 11
字面量类型
let str1 = 'hello' //string类型
const str2: 'hello' = 'hello' //’hello'类型 只能是‘hello’
let age: 18 = 18 //18类型 只能是18
// 字面量类型 配合 联合类型一起使用。用来表示一组明确的可选值列表。
function changeDirection(direction: 'up' | 'down' | 'left' | 'down' ) {}
changeDirection('up') // 参数只能是四个其中一个
枚举enum
ts中的枚举
语法:enum 枚举名称{成员1,成员2....};
把枚举成员的值为数字的枚举,称为:数字枚举。
枚举成员是有值的,默认为:从0开始自增的数值。
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
把枚举成员的值为字符串的枚举,称为:字符串枚举。
字符串枚举没有自增长行为,因此,字符串枚举的每个成员必须有初始值
enum gender {
male = '1', // gender.male = "1"
female = '0', // gender.female = "0"
}
可以给枚举中的成员初始化值。也可以全部赋值
enum Direction {
Up = 22,
Down, // 23
Left, // 24
Right // 25
}
enum Direction {
Up = 2,
Down = 4,
Left = 6,
Right = 8
}
举例
enum Direction {
Up,
Down,
Left,
Right
}
function changeDirection(direction: Direction) {
console.log(direction);
}
// 通过枚举类型的点传参
changeDirection(Direction.Up)
如果想在ts中 表示一组明确的可选值 ,有两种方案,一种是字面量+联合类型组合(🌟更直观),一种是枚举类型
any类型
/* any类型 */
let obj: any = {x: 5}
这样均不报错
// 访问不存在的属性 或者 赋值
obj.y = 10
// 当作函数调用
obj()
// 赋值给其他类型的变量
let arr: string[] = obj
隐式具有any类型
// 声明变量不提供类型也不提供默认值
let a
// 函数参数不加类型
function(num1, num2)
类型判断typeof
可以在 类型上下文 中引用 变量 或 属性 的类型(类型查询)
- 使用typeof操作符来获取变量str1的类型,结果与第一种(对象字面量形式的类型)相同。
- typeof出现在类型注解的位置(参数名称的冒号后面)所处的环境就在类型上下文(区别于JS代码)。
- 注意:typeof只能用来查询变量或属性的类型,无法查询其他形式的类型(比如,函数调用的类型)。
// 自动类型判断
let num = {num1: 2, num2: 5}
let str1 = "Hello TS"
console.log(typeof num) // object
console.log(typeof num.num1) // number
console.log(typeof str1) // string
// 利用 typeof 简写类型
let num = {num1: 2, num2: 5}
function add(num: {num1: number, num2: number}) {
return num.num1 + num.num2
}
function add2(ccc: typeof num ) {
return ccc.num1 + ccc.num2
}
console.log(add({num1: 1, num2: 2})); // 3
console.log(add2({num1: 1, num2: 2})); // 3
🌟TypeScript高级类型
- class类
- 类型兼容性
- 交叉类型
- 泛型 和 keyof
- 索引签名类型 和 索引查询类型
- 映射类型
class
定义
class Person {
age: number
gender = '男'
}
let p = new Person()
p.age = 10;
声明成员age,类型为number(没有初始值)。
声明成员gender,并设置初始值,此时,可省略类型注解(TS类型推论为string类型)
构造函数 ( 不需要 返回值类型)
class Person2 {
age: number
gender: string
constructor(age: number, gender: string) {
this.age = age;
this.gender = gender;
}
}
let p2 = new Person2(10, '男');
console.log(p2.age, p2.gender);
成员初始化(比如,age:number)后,才可以通过this.age来访问实例成员。
需要为构造函数指定类型注解,否则会被隐式推断为any;构造函数不需要返回值类型。
实例方法 ( 需要 返回值类型)
class Point {
x = 2
y = 4
scale(n: number): void {
this.x *= n
this.y *= n
}
}
const p = new Point()
p.scale(10)
console.log(p.x, p.y) // 20 40
类的继承-extends(继承父类)
class Animal {
move(){
console.log('走路');
}
}
class Dog extends Animal {
bark(){
console.log('汪汪');
}
}
const dog = new Dog()
dog.bark()
dog.move(
类的继承-implements(实现接口)
interface Singale {
name: string
sing(): void
}
class Person implements Singale {
name: string // = '张三'
sing() {
console.log('唱歌');
}
}
类成员可见性
可见性修饰符包括:public(公有的) protected(受保护的) private(私有的)
常见修饰符:readonly(只读修饰符)
public
class Animal {
public move() {console.log('走路');}
}
const a = new Animal()
a.move()
// 子类继承父类
class Dog extends Animal {
bark() {
console.log('汪汪');
}
}
const dog = new Dog()
dog.move()
默认是公有 可省略
protected
class Animal {
protected move() {console.log('Moving along');}
}
class Dog extends Animal {
bark() {
console.log('汪汪');
this.move();
}
}
const dog = new Dog();
dog.bark();
// dog.move(); error
只能在类内部和子类中使用(即可以在类中其他的方法内使用),不能被实例对象调用
private
class Animal {
private __run__ () {
console.log('Animal 内部辅助函数')
}
// 受保护的
protected move() {
this.__run__()
console.log('走路')
}
// 公开的
run() {
this.move()
this.__run__()
console.log('跑步');
}
}
const a = new Animal()
// a.__run__() // error private只能在类内部使用
// 子类
class Dog extends Animal {
bark() {
console.log('汪汪');
this.move(); // protected 在 类内部 和 子类 中可以使用
// this.__run__() // error private只能在 类内部 使用
}
}
const dog = new Dog();
dog.bark();
dog.run();
// dog.move() // error protected 只能在 类内部 和 子类 中可以使用
// dog.__run__() // error private只能在 类内部 使用
只能在类内部使用 子类和实例对象均不可使用
readonly
readonly 只能修饰属性 不能修饰方法
class Person {
readonly age: number = 18
constructor(age: number) {
this.age = age
}
}
readonly 表示只读,用来防止在构造函数之外对属性进行赋值
————————————————————
class Person {
// 注意:只要是readonLy来修饰的属性,必须手动提供明确的类型
readonly age: number
constructor(age: number) {
this.age = age
}
}
可以给赋初始值,初始值如果不加类型注解,则属性的类型即为值(字面量类型)
————————————————————
interface IPerson {
readonly name: string
}
let obj: IPerson = {
name: '张三'
}
// obj.name = '李四' // error
接口中的使用
————————————————————
let obj1: { readonly name: string} = {
name: '张三'
}
l的使用
类型兼容性
有两种类型系统
- 结构化类型系统(TS)(也叫 duck typing 鸭子类型)
- 表明类型系统
class Point {
x: number
y: number
}
class Point2D {
x: number
y: number
}
let p: Point = new Point2D()
在结构类型系统中,如果两个对象具有相同的形状,则认为它们属于同一类型。
class Point3D {
x: number
y: number
z: number
}
let p1: Point = new Point3D()
对于对象类型来说,y的成员至少与x相同,则×兼容y(成员多的可以赋值给少的)
接口兼容性
interface Point {x: number; y: number}
interface Point2D {x: number; y: number}
interface Point3D {x: number; y: number; z: number}
let p1: Point
let p2: Point2D
let p3: Point3D
// 正确
p1 = p2
p2 = p1
p1 = p3
p2 = p3
// p3 = p1 p3 = p2 error
和class类似
————————————————————
class Point4D {
x: number
y: number
z: number
}
p2 = new Point4D()
类可以和接口兼容
函数兼容性
type F1 = (a: number) => void
type F2 = (a: number, b: number) => void
let f1 = F1
let f2 = F2
f2 = f1
1、参数个数:参数少的可以赋值给参数多的
————————————————————
type F1 = (a: number) => void
type F2 = (a: number) => void
let f1 = F1
let f2 = F2
f1 = f2
2、参数类型(原始类型):相同位置的参数类型要相同或兼容
————————————————————
interface Point2D {
x: number
y: number
}
interface Point3D {
x: number
y: number
z: number
}
type F2 = (p: Point2D) => void // 相当于有两个参数
type F3 = (p: Point3D) => void // 相当于有三个参数
let f2 = F2
let f3 = F3
f3 = f2
2、参数类型(对象类型):将对象(对象或者接口)拆开,把每个属性看作一个参数,参数少的可以赋值给参数多的
————————————————————
type F5 = () => number
type F6 = () => number
let f5 = F5
let f6 = F6
f5 = f6
3、返回值类型(原始类型): 一样即可互相赋值
————————————————————
type F7 = () => {x: number}
type F8 = () => {x: number, y: number}
let f7 = F7
let f8 = F8
f7 = f8
3、返回值类型(对象类型): 成员多的赋值给成员少的
交叉类型(&)
interface A {
name: string
}
interface B {
age: number
}
let obj4: A & B = {
name: '张三',
age: 18
}
用于组合多个类型为一个类型(常用于对象类型,interface or class,type)
交叉类型(&) 和 接口继承(extends) 的对比
- 相同点:都可以实现对象类型的组合
- 不同点:两种方式实现类型组合时,对于 同名属性和方法 之间,处理类型冲突的方式不同
interface A {
name: string
fn:(value: number) => string
}
interface B extends A{
name: number
fn:(value: string) => string
}
接口继承同名 方法或属性 会报错(类型不兼容)
————————————————————
interface A {
name: string
fn:(value: number) => string
}
interface B {
name: number
fn:(value: string) => string
}
type C = A & B // c的类型是 A&B
交叉类型没有错误 可以简单理解成
name: string | number
fn: (value: string | number) => string
泛型<>
定义
语法:在 函数/对象/接口 名称的后面添加<>,尖括号中指定具体的类型
通过泛型做到了让函数与多种不同的类型一起工作,实现了复用的同时保证了类型安全
定义:
function id<Type>(value: Type): Type {return value}
调用:
function id<类型>(参数)
// 使用泛型来创建一个函数
function id<Type>(value: Type): Type {
return value
}
// 调用时传入类型参数
const num = id<number>(2)
const str = id<string>('hello')
// 调用时不传入类型参数,TypeScript 会自动推断类型参数(字面量类型)
const num1 = id(2)
const str1 = id('hello')
泛型约束 extends
添加泛型约束收缩类型,主要有以下两种方式:1、指定更加具体的类型 2、添加约束
function id1<Type>(value: Type[]): Type[] {
value.length
return value
}
1、指定更加具体的类型
————————————————————
interface ILength {
length: number
}
// 使用 extends 关键字来约束 Type 必须符合接口
function id2<Type extends ILength>(value: Type): Type {
value.length
return value
}
id2('hello')
id2([1, 2, 3])
id2({ length: 10, value: 3 })
2、添加约束
为泛型添加 类型约束(使用 keyof)
function getProperty<Type, Key extends keyof Type
>(obj: Type, key: Key) {
return obj[key]
}
getProperty({name: '张三', age: 18}, 'name')
getProperty({name: '张三', age: 18}, 'age')
// 补充 (了解)
getProperty(18, 'toString')
getProperty('hello', 'slice')
getProperty('hello', 1) // 1代表索引,取得是数组中索引为1的值
getProperty([1, 2, 3], 'length')
getProperty([1, 2, 3], 1) // 1代表索引,取得是数组中索引为1的值
1.添加了第二个类型变量Key,两个类型变量之间使用(,)逗号分隔。
2. keyof关键字接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型.
3. 示例中 keyof Type 实际上获取的是 person 对象所有键的联合类型,也就是:'name'|'age'。
4. 类型变量 Key 受 Type 约束,可以理解为:Key只能是Type所有键中的任意一个,或者说只能访问对象中存在的属性。
泛型接口
interface IdFunc<Type> {
id: (value: Type) => Type
ids: () => Type[]
}
let obj: IdFunc<number> = {
id(value) {
return value
},
ids() {
return [1, 2, 3]
}
}
接口的类型变量,对接口中所有其他成员可见
泛型接口在被调用时必须 显式的指定 具体的 类型(IdFunc<number>)
泛型类
class GenericNumber<NumType> {
defaultValue: NumType
add: (x: NumType, y: NumType) => NumType
}
// 这种情况下,推荐明确指定 <类型>
const myNum = new GenericNumber<number>()
myNum.defaultValue = 50
在 new 实例对象时把 number 传进去,如果想省略传入类型,则需要加构造函数
————————————————————
class GenericNumber<NumType> {
defaultValue: NumType
add: (x: NumType, y: NumType) => NumType
constructor(value: NumType) {
this.defaultValue = value
}
}
// 此时,可以省略 <类型> 不写,类型推断出是 number
const myNum = new GenericNumber(50)
// 仍可以重新赋值
myNum.defaultValue = 50
泛型——内置工具类型
TS内置了一些常用的工具类型,来简化TS中的一些常见操作。
在type中使用
- Partial<Type>
- 用来构造(创建)一个新类型,将Type的所有属性设置为(?) 可选
- Readonly<Type>
- 用来构造一个类型,将Type的所有属性都设置为 readonly(只读)
- Pick<Type,Keys>
- 从Type中选择属性来构造新类型
- Record<Keys,Type>
- 构造一个对象类型,属性键为Keys,属性类型为Type
1、Partial 可选
interface Props {
name: string
children: number[]
}
type PartialProps = Partial<Props>
let p1: PartialProps = {}
// type PartialProps = {
// name?: string | undefined;
// children?: number[] | undefined;
// }
2、readonly 只读
interface Props {
name: string
children: number[]
}
type ReadonlyProps = Readonly<Props>
let p2: ReadonlyProps = {
name: '张三',
children: [1, 2, 3]
}
// p2.children = [5,6,8] error
3、Pick 选择属性
interface Props {
name: string
phone: number
children: number[]
}
type PickProps = Pick<Props, 'name' | 'phone'>
// type PickProps = {
// name: string;
// phone: number;
// }
4、Record 构造一个新的对象类型
type RecordProps = Record<'a' | 'b' | 'c', number[]>
let p4: RecordProps = {
a: [1],
b: [2],
c: [3]
}
// 赋值时 abc 必须都存在
// type RecordProps = {
// a: number[];
// b: number[];
// c: number[];
// }
索引签名类型[key: 类型]
当 **无法确定 **对象中有哪些 属性或方法 (或者说对象中可以出现任意多个属性),此时,就可以用 索引签名类型 了
- 使用[key:string]来约束该接口中允许出现的属性名称。表示只要是string类型的属性名称,都可以出现在对象中。
- 这样,对象obj中就可以出现任意多个属性(比如,a、b等)。
- key只是一个占位符,可以换成任意合法的变量名称。
- JS中对象({})的键是string类型的。
使用
interface AnyObject {
[key: string]: number // 键是string 说明是object对象
}
let obj3: AnyObject = {
a: 999,
bbb: 18
}
// let obj4: AnyObject = [111, 222, 333] error
// 不能将类型“number[]”分配给类型“AnyObject”。
// 类型“number[]”中缺少类型“string”的索引签名
————————————————————
interface MyArray<T> {
[index: number]: T // 键是number 说明是数组
}
let arr4: MyArray<number> = [1, 2, 3, 4, 5]
arr4[2] // 3
// 索引签名类型在数组中的应用
// 数组的键(索引)是数值类型
映射类型[Key in Type]
基于 旧类型 创建 新类型(对象类型)
比如,类型PropKeys有x/y/z,另一个类型Type1中也有x/y/z,并且Type1 中x/y/z的类型相同
type Propkeys = 'x' | 'y' | 'z'
type Type1 = { x: number; y: number; z: number }
这样书写没错,但x/y/z重复书写了两次。像这种情况,就可以使用映射类型来进行简化。
type Propkeys = 'x' | 'y' | 'z'
type Type2 = { [Key in PropKeys]: number }
- 映射类型是基于索引签名类型的,所以,该语法类似于索引签名类型,也使用了[]。
- Key in PropKeys 表示 Key 可以是 PropKeys 联合类型中的任意一个,类似于 forin(let k in obj)。
- 使用映射类型创建的新对象类型Type2和类型Type1结构完全相同。
- 注意:映射类型只能在 类型别名 中使用,不能在接口中使用。
使用 —— 基于 联合类型 创建 新类型
type Propkeys = 'x' | 'y' | 'z'
type Type1 = { x: number; y: number; z: number }
type Type2 = { [k in Propkeys]: number } // // 赋值时 xyz 必须都存在
// type Type2 = {
// x: number;
// y: number;
// z: number;
// }
// 类型“{ x: number; }”缺少类型“Type2”中的以下属性: y, z
// let obj2: Type2 = {x: 5}
使用 —— 基于 对象类型 创建 新类型 keyof
type Props = { a: number; b: string; c:boolean}
type Type3={[key in keyof Props]: number }
// 1.首先,先执行 keyof Props 获取到对象类型 Props 中所有键的联合类型即,'a'|"b'|'c'。
// 2.然后,Key in... 就表示 Key 可以是 Props 中所有的键名称中的任意一个。
// type Type3 = {
// x: number;
// y: number;
// z: number;
// }
索引查询类型
// 查询索引的类型
type Props = { x: number; y: string; z: boolean }
type TypeA = Props['x'] // number
type TypeB = Props['y'] // string
type TypeC = Props['z'] // boolean
// 模拟 Partial 类型
type MyPartial<T> = {
[P in keyof T]?: T[P]
}
type PartialProps = MyPartial<Props>
索引查询类型 ——同时查询多个索引的类型
type TypeD = Props['x' | 'y'] // number | string
type TypeE = Props[keyof Props] // number | string | boolean
🌟TypeScript类型声明文件
ts 中有两种文件类型:
- .ts 文件
- .d.ts 文件 【为 JS 库提供类型信息】
.ts 文件:
既包含类型信息又可执行代码。
可以被编译为js文件,然后,执行代码。
用途:编写程序代码的地方。
.d.ts 文件:
只包含类型信息的类型声明文件。
不会生成js文件,仅用于提供类型信息。
用途:为JS提供类型信息。
第三方库的类型声明文件
- 库自带类型声明文件
- 由DefinitelyTyped提供
DefinitelyTyped是一个github仓库,用来提供高质量TypeScript类型声明。
可以通过npm/yarn来下载该仓库提供的TS类型声明包,这些包的名称格式为:@types/*。
当安装@types/*类型声明包后,TS会 自动加载 该类声明包,以提供该库的类型声明。
创建自己的类型声明文件
- 项目内共享类型
- 为已有JS文件提供类型声明
1、项目内共享类型
1.使用场景:
如果多个.ts文件中都用到同一个类型,此时可以创建.d.ts文件提供该类型,实现类型共享
2.实现:
- 创建index.d.ts类型声明文件。
- 创建需要共享的类型,并使用export导出(Ts中的类型也可以使用import/export实现模块化功能)。
- 在需要使用共享类型的.ts文件中,通过import导入即可(.d.ts后缀导入时,直接省略
3.使用:
index.d.ts 文件
type Props = { x: number; y: number; }
export { Props };
a.ts 文件
import { Props } from "./index"; // 导入时,不需要加后缀[.d.ts]
let p1: Props = { x: 1, y: 2 };
2、为已有JS文件提供类型声明
1.使用场景:
在将JS项目迁移到TS项目时,为了让已有的js文件有类型声明。
成为库作者,创建库给其他人使用。
2.实现:
类型声明文件的编写与模块化方式相关
declare
declare
关键字用于声明外部的变量、模块、函数或类型,帮助 TypeScript 识别在运行时存在但在编译时未知的内容。
声明全局变量
// 声明一个全局变量 declare var $: any; // 使用全局变量 $('body').addClass('active'); declare var jQuery: (selector: string) => any; jQuery('#foo');
声明全局函数
declare function myGlobalFunction(name: string): void; myGlobalFunction('Hello World');