
TypeScript介绍
- TypeScript(简称:TS)是 JavaScript 的超集(JS 有的 TS 都有)。
- TypeScript = Type + JavaScript(在 JS 基础之上,为 JS 添加了类型支持)。
- TypeScript 是微软开发的开源编程语言,可以在任何运行 JavaScript 的地方运行
1. TS安装与编译
- 第一步:全局安装ts和ts-node
// 全局安装ts
npm install typescript -g
// 全局安装ts-node
npm install ts-node@8.5.4 -g
- 第二步:生成tsconfig.js配置文件
tsc --init
- 第三步:创建
index.ts文件
const str: string = 'hello ts'
console.log(str)
- 第四步:编译 ts 为 js 在控制台输入命令
tsc index.ts
当前文件下会生成一个同名的index.js文件 这时候已经把ts文件编译成了一个js文件了;
但是这样非常的麻烦,每次都需要运行tsc ts文件名 生成js文件,再去使用node js文件名编译出结果。可以使用全局安装的 ts-node 直接编译 ts 文件
tsc-node index.ts // 输入 hello ts
// 这样运行后,修改ts文件后 ,帮我们自动编译成js文件
tsc --watch index.ts
2. TS原始类型
2.1 String 类型
let myName: string = 'lazy-zheng'
2.2 Number 类型
let age : number = 20
2.3 布尔类型(boolean)
let isLoading: boolean = false
2.4 null类型
let n: null = null
2.5 undefined 类型
let u: undefined = undefined
2.6 symbol 类型
let s: symbol = Symbol()
2.7 数组类型
// 数组类型的两种写法,推荐使用写法1
// 写法1:
let numbers1: number[] = [1, 3, 5];
let strings1: string[] = ['a', 'b', 'c', 'd'];
// 写法2:
let numbers2: Array<number> = [2, 4, 6];
let strings2: Array<string> = ['e', 'f', 'g', 'h'];
2.8 联合类型
// 联合类型多个类型之间使用 | 隔开
let arr: (number | string)[] = [1, 3, 5, 'a', 'b'];
2.9 类型别名
// 当同一类型(复杂)被多次使用到,可以使用类型别名,简化该类型的使用,关键字为type
let arr1: (number | string)[] = [1, 3, 5, 'a', 'b'];
let arr2: (number | string)[] = [2, 4, 6, 'c', 'd'];
// 改造
type CustomArray = (number | string)[];
let arr3: CustomArray = [1,'a',3,'b'];
let arr4: CustomArray = [2,'c',4,'d'];
2.10 函数类型
function sum1(num1: number,num2:number):number {
return num1 + num2;
}
console.log(sum1(2, 6));
const sum2 = (num1: number,num2:number):number => {
return num1 + num2;
}
console.log(sum2(3, 2));
// 同时指定参数和返回值类型,这种写法仅支持箭头函数。
const sum3: (num1: number, num2: number) => number = (num1,num2) => {
return num1 + num2;
}
console.log(sum3(1,2));
// 如果函数没有返回值,那么函数的返回值类型为 void
function hello(name: string): void {
console.log(`hello ${name}`);
}
hello('正');
// 函数可选参数
// 注意:可选参数必须放到参数的最后面,也就是说可选参数后面不能再出现必选参数
function mySlice(start?: number, end?: number): void {
console.log('起始索引:', start, '结束索引:', end);
}
mySlice();
mySlice(1);
mySlice(3, 6);
2.11 对象类型
// 1.在一行代码中指定对象的多个属性类型时,使用;(分号)分割;
// 2.如果一行代码只指定一个属性类型(通过换行来分割多个属性类型),可以去掉;(分号);
let person: { uname: string; age: number; sayHello(): void } = {
uname:'正',
age: 18,
sayHello() {
console.log('Hello')
}
}
// 可选属性的语法与函数可选参数的语法一致,使用?来表示
function myAxios(config: { url: string; method?: string }) {
console.log(config);
}
myAxios({
url:'123'
})
2.12 接口
// 当一个对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的目的
interface IPerson {
name: string,
age: number,
sayHi(): void
}
const person1: IPerson = {
name: '张三',
age: 18,
sayHi() {
console.log(this.name,'person1')
},
}
const person2: IPerson = {
name: '李四',
age: 16,
sayHi() {
console.log(this.name,'person2')
}
}
接口和类型别名对比
- 相同点:都可以给对象指定类型。
- 不同点:
- 接口,只能为对象指定类型。
- 类型别名,可以为任意类型指定别名。
interface IPerson {
name: string,
age: number,
sayHi(): void
}
type IPerson = {
name: string,
age: number,
sayHi(): void
}
type NumAndStr = number | string
2.13 接口继承
interface Point2D {
x: number,
y:number
}
// 使用extends关键字实现继承
interface Point3D extends Point2D {
z:number
}
const point: Point3D = {
x: 1,
y: 2,
z: 3
}
2.14 元组
场景:在使用经纬度坐标来标记位置信息;可以使用数组来记录坐标,该数组中只有两个元素,并且这两个元素都是number类型。
// 这种方式可以满足需求,但是不够严谨
const position1: number[] = [39, 112];
// 元组类型时另一种类型的数组,它确切地指导包含多少个元素,以及特定索引对应的类型
const position2: [number, number] = [39.5516, 114.2238];
2.15 类型推论
在TS中,某些没有明确指出类型的地方,TS的类型推论机制会帮助提供类型。
由于类型推论的存在,这些地方,类型注解可以省略不写。
发生类型推论的两种场景:
声明变量并初始化时决定函数返回值时
// 此时ts自动推断出变量a为number类型
let a = 18;
// 此时不会触发类型推论
let b;
b = 1
b = '6'
// 函数的入参推荐必须指定类型,函数返回值会触发类型推论,推断出函数返回值的类型
function sum(num1: number, num2: number) {
return num1 + num2;
}
2.16 类型断言
- 使用
as关键字实现类型断言。 - 关键字as后面的类型是一个更加具体的类型(HTMLAnchorElement是HTMLElement的子类型)。
- 通过
类型断言, link的类型变得更加具体,这样就可以访问a标签特有的属性或方法了。
const link = document.getElementById('link') as HTMLAnchorElement;
link.href = 'www.baidu.com';
2.17 字面量类型
使用模式:字面量类型配合联合类型一起使用。
使用场景:用来表示一组明确的可选值列表。
// 字面量类型
function changeDirection(direction:'up' | 'down' | 'left' | 'right') {
console.log(direction);
}
changeDirection('down');
2.18 枚举
枚举的功能类似于字面量类型+联合类型组合的功能,也可以表示一组明确的可选值。
枚举:定义一组命名常量。它描述一个值,该值可以是这些命名常量中的一个。
// 枚举
// 枚举默认不设置值的时候,默认为当前枚举的索引
enum Direction{
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT'
}
function changeDirection(direction: Direction) {
console.log(direction) // => 'LEFT'
}
changeDirection(Direction.Left)
- 其他类型仅仅被当做类型,而
枚举不仅用作类型,还提供值(枚举成员都是有值的)也就是说,其他类型会在编译为js代码时自动移除,但是,枚举类型会被比编译为js代码! - 一般情况下,
推荐使用字面量类型 + 联合类型组合的方式,因为相比枚举,这种方式更加直观,简洁,高效。
2.19 typeof运算符
js中提供了typeof操作符,用来在js中获取数据的类型
console.log(typeof 'hello ts') // => string
实际上,ts中也提供了typeof操作符:可以在类型上下文中引用变量或属性的类型(类型查询)。
使用场景:根据已有变量的值,获取该值的类型,来简化类型书写。
let p = {
x: 1,
y: 2
};
function formaPoint1(point:{x:number,y:number}) {
console.log(point)
}
// 这个写法等同于上面的写法
function formaPoint2(point: typeof p) {
console.log(point);
}
let num: typeof p.x; // => num变量的类型为number
注意:typeof只能用来查询变量或属性的类型,无法查询其他形式的类型(比如,函数调用的类型)
3.TS高级类型
3.1 class基本使用
class Person {
// 没有默认值
age: number;
gender = '男';
x = 1;
y = 2;
// 不允许给constructor设置返回值类型,否则报错提示
constructor(age:number, gender:string) {
this.age = age;
this.gender = gender;
}
// 实例方法
scale(n: number) {
this.x *= n;
this.y *= n;
};
}
const p = new Person(18, '男');
p.scale(10)
3.2 class继承
// 1. extends关键字
class Animal {
move() {
console.log('走几步')
}
}
class Dog extends Animal {
name = '哈士奇';
bark() {
console.log('旺旺~')
}
}
const d = new Dog();
d.move();
d.bark();
console.log(d.name);
// 2.implements 实现接口
interface Singable {
myName: String,
sayHi(): void,
}
class Person implements Singable {
myName = '正';
sayHi() {
console.log('Say Hi')
}
}
- 通过implements关键字让class实现接口。
- Person类实现接口Singable意味着,Person类中必须提供Singable接口中指定的属性和方法。
3.3 class类修饰符
// 1.class类的可见性修饰符
class Animal {
constructor() {
}
// public 公有的,公有成员可以被任意地方访问,默认可见性
public move() {
console.log('走几步~');
}
// protected 受保护的,仅对其声明所在类和子类中(非实例对象)可见
protected run() {
this.__jump__();
console.log('跑起来~');
}
// private 表示私有的,只有在当前类中可见,对实例对象以及子类也是不可见的
private __jump__() {
console.log('跳起来~');
}
}
class Dog extends Animal {
// 属性前面加readonly关键字,当前属性就变为只读属性了,不可以被修改
readonly age: number = 18;
constructor(age: number) {
super();
this.age = age;
}
bark() {
console.log('旺旺~')
this.move();
}
}
3.4 类型兼容性
两种类型系统:
- Structural Type System (结构化类型系统)
- Nominal Type System (标明类型系统)
TS采用的是结构化类型系统,也叫做duck typing(鸭子类型),类型检查关注的是值所具有的形状,也就是说,在结构化类型系统中,对于对象类型来说,A的成员至少与B相同,则B兼容A(成员多的可以赋值给少的)。
对象的类型兼容性
// 两个类的兼容性演示:
class Point {
x: number;
y: number;
}
class Point2D{
x: number;
y: number;
z: number;
}
// 这样编写不会报错
const p: Point = new Point2D();
- 变量p的类型被标注为Point类型,但是,它的值确实Point2D的实例,并没有类型错误;
- 因为TS是结构化类型系统,只检查Point和Point2D的结构是否相同;(都具有x,y属性,且属性类型也相同);
- 接口interface之间的兼容性,类似于class,并且,class和interface之间也可以兼容;
- 如果在Nominal Type System (标明类型系统)中(例如:C#、Java等)它们是不同的类,类型无法兼容。
函数之间的兼容性
函数之间兼容性比较复杂,需要开率:1. 参数个数 2.参数类型 3. 返回值类型参数个数,参数多的兼容参数参数少的(或者说,参数少的可以赋值给多的)
// 1.参数个数:参数少的可以赋值给参数多的
type F1 = (a: number) => void;
type F2 = (a: number, b: number) => void;
let f1: F1 = (a:number) => {
console.log(a)
};
let f2: F2;
f2 = f1;
// 错误演示
// f1 = f2;
- 参数少的可以赋值给参数多的,所以f1可以赋值给f2;
- 在js中省略用不到的函数参数实际上是很常见的,这样的使用方式,促成了TS中函数类型之间的兼容性;
- 并且因为回调函数是有类型的,所以,TS会自动推导出参数item、index、array的类型。
返回值类型
// 原始类型
type F5 = () => string;
type F6 = () => string;
let f5: F5;
let f6: F6 = f5;
// 对象类型
type F7 = () => { name: string };
type F8 = () => { name: string, age: number };
let f7: F7;
let f8: F8
f7 = f8;
- 如果返回值类型是原始类型,此时两个类型要相同,比如,F5和F6;
- 如果返回值类型的对象类型,此时成员多的可以赋值给成员少的,比如,F7和F8;
3.5 交叉类型
交叉类型(&):功能类似于接口继承(extends),用于组合多个类型为一个类型(常用于对象类型)。
interface Person {
name: string
};
interface Contact {
phone: string
};
type PersonDetail = Person & Contact
let obj: PersonDetail = {
name: 'zz',
phone:'159********'
}
- 使用交叉类型后,新的类型PersonDetail就同时具备了Person和Contact的所有属性类型
交叉类型(&)和接口继承(extends)的对比:
- 相同点:都可以实现对象类型的组合。
- 不同点:两种方式实现类型组合时,对于
同名属性之间,处理类型冲突的方式不同。
interface A{
fn: (value: number) => string;
}
// 报错
interface B extends A {
fn: (value: string) => string;
}
interface A {
fn: (value: number) => string;
}
interface B {
fn: (value: string) => string;
}
type C = A & B;
// C相当于 fn: (value: number | string)=> string
3.6 泛型的基本使用
泛型是可以在保证安全前提下,让函数等与多种类型一起工作,从而实现复用,常用于:函数、接口、class中
// 使用泛型来创建一个函数
function id2<Type>(value:Type): Type {
return value;
}
// 1. 以number类型调用泛型函数
const num = id2<number>(18);
// 2. 以string类型调用泛型函数
const str = id2<string>('zz');
// 3. 以boolean类型调用泛型函数
const bool = id2<string>('zz');
// --
// 调用泛型函数的时候类型可以省略
let num1 = id(20);
let str1 = id('test');
- 泛型在保证类型安全(不丢失类型信息)的同时,可以让函数与多种不同的类型一起工作,灵活复用;
- 在调用泛型函数时,
可以省略<类型>来简化泛型函数的调用;
3.7 泛型约束
添加泛型约束收缩类型,主要有两种方式:
- 指定更加具体的类型
function id<Type>(value: Type[]): Type[]{
value.length;
return value;
}
- 添加约束
interface ILength {
length:number
}
function id<Type extends ILength>(value:Type) : Type {
console.log(value.length, 'len');
return value;
};
id(['a','b']);
id('test');
id({ length: 1, name: 'zz' });
- 创建描述约束的接口Ilenth,该接口要求提供length属性;
- 通过
extends关键字使用该接口,为泛型(类型变量)添加约束; - 该约束表示:
传入的类型必须具有length属性
3.8 多个泛型变量的情况(使用keyof)
泛型的类型变量可以有多个,并且类型变量之间还可以约束(比如,第二个类型变量受第一个类型变量的约束)。
function getProp<Type,Key extends keyof Type>(obj:Type, key:Key) {
return obj[key];
}
let person = { name: 'zz', age: 18, say:()=> {} };
getProp(person, 'name');
getProp(person, 'age');
getProp(person, 'say');
// 补充:几乎用不到这种场景
getProp(18, 'toFixed');
getProp('zz', 'split');
getProp('zz', 1);
getProp(['x', 'y', 'z'], 1);
- 添加了第二个类型变量Key,两个类型变量之间使用(,)逗号分隔;
keyof关键字接收一个对象类型,生成其键名称(可能是字符串或者数字)的联合类型。
3.9 泛型接口
泛型接口:接口也可以配合泛型来使用,以增加其灵活性,增强其复用性
interface IdFunc<Type> {
id: (value: Type) => Type,
ids: () => Type[]
}
let obj: IdFunc<number> = {
id(value) {
return value;
},
ids() {
return [1,2,3]
}
}
- 在接口名称后面增加
<类型变量>,那么,这个接口就变成了泛型接口; - 接口的类型变量,对接口中所有其他成员可见,也就是
接口所有成员都可以使用泛型变量 - 使用泛型接口时,需要显式指定具体的类型。
3.10 泛型类
class GenericNumber <NumType>{
defalutVlaue: NumType
add: (x: NumType, y: NumType) => NumType
constructor(value: NumType) {
this.defalutVlaue = value;
}
}
const myNum = new GenericNumber<number>(100);
// myNum.defalutVlaue = 10;
- 类似于泛型接口,在class名称后面添加
<类型变量>,这个类就变成了泛型类; - 当初始化类的时候传递的参数,此时可以
省略调用时候的类型变量new GenericNumber(100);
3.11 泛型工具类型
Partial<Type>用来构造(创建)一个类型,将Type的所有属性设置为可选,不会改变原来的类型
interface Props {
id: string
children:number[]
}
type PartialType = Partial<Props>
let p1: Props = {
id: 'abc',
children: [1, 2]
}
let p2: PartialType = {
}
Readonly<Type>用来构造一个类型,将Type的所有属性设置为readonly(只读)
interface Props {
id: string
children:number[]
};
type ReadonlyProps = Readonly<Props>;
let p1: ReadonlyProps = {
id: '1',
children: [1, 2, 3]
}
// 重新给id属性赋值时,就会报错:无法分配到"id",因为它是只读属性
p1.id = '123'
Pick<Type,Keys>从Type中选择一组属性来构造新类型
interface Props {
id: number,
title: string,
content:string,
};
type PickProps = Pick<Props, 'title' | 'content'>;
let p1: PickProps = {
title: 'zz',
content: '内容'
};
(1)Pick工具类型有两个类型变量: 1 表示选择谁的属性 2 表示选择哪几个属性;
(2)其中第二个类型变量,如果只选择一个则只传入该属性名即可;
(3)第二个类型变量传入的属性只能是第一个类型变量中存在的属性;
Record<Keys,Type>构造一个对象类型,属性键为Keys,属性类型为Type
type RecordObj = Record<'a' | 'b' | 'c', string[]>
let obj: RecordObj = {
a: ['a'],
b: ['b'],
c: ['c'],
}
(1)Records工具类型有两个类型变量:
1 表示对象有哪些属性 2 表示对象属性的类型;
(2)构建的新对象RecordObj表示:这个对象有三个属性分别为a/b/c,属性值的类型都是string[];
3.12 索引签名类型
当无法确定对象中有哪些属性(或者说对象中可以出现任意多个属性),此时,就用到索引签名类型了;
interface AnyObject {
[key:string]:number
};
let obj: AnyObject = {
a: 1,
b: 2
};
interface MyArray<T> {
[n: number]: T
}
let arr: MyArray<number> = [1, 3, 2];
(1)使用[key:string]来约束该接口中允许出现的属性名称,表示只要是string类型的属性名称,都可以出现在对象中;
(2)key只是一个占位符,可以换成任意合法的变量名称;
(3)MyArray接口模拟原生的数组接口,并使用[n:number]来作为索引签名类型;
3.13 映射类型
映射类型:基于旧类型创建新类型(对象类型),减少重复,提高效率;
type PropKeys = 'x' | 'y' | 'z';
type Type1 = { x: number, y: number, z: number };
// Type2等同于Type1
type Type2 = { [key in PropKeys]: number };
// 错误演示:
interface Type3 {
[key in PropKeys]: number
}
(1)映射类型是基于索引签名类型的,所以,该语法类似于索引签名类型,也使用了[];
(2)Key in PropKeys表示Key可以是PropKeys联合类型中的任意一个,类似于for in(let k in obj);
(3)注意:映射类型只能在类型别名中使用,不能在接口中使用;
映射类型除了根据联合类型创建新类型外,还可以根据对象类型来创建:
type Props = { a: number, b: string, c: boolean };
type Type1 = { [key in keyof Props]: string };
(1)先执行keyof Props获取到对象类型Props中所有键的联合类型即,'a'|'b'|'c';
(2)然后,key in...就表示Key可以是Props中所有的键名称中的任意一个;
3.14 索引查询类型
type Props = { a: number, b: string, c: boolean };
type TypeA = Props['a'];
// 实现Partial工具
type MyPartial<T> = {
[P in keyof T]?: T[P];
}
type MyPartialProps = MyPartial<Props>
// 其他使用方式:同时查询多个类型
type TypeB = Props['a' | 'b'];
// keyof Props返回所有属性的的类型
type TypeC = Props[keyof Props];
(1)用到T[P]语法,在TS中叫做索引查询(访问)类型;
(2)Props['a']表示查询类型Props中属性'a'对应的类型number,所以TypeA的类型为number;
4. 类型声明文件
4.1 类型声明文件概述
- 在项目开发中使用第三方库时,你会发现它们几乎都有相应的TS类型,这些类型是怎么来的呢?
类型声明文件 类型声明文件:用来为已存在的JS库提供类型信息这样在TS项目中使用这些库时,就向用TS一样,都会有代码提示、类型保护等机制了。- TS中有两种文件类型:1.
ts文件2..d.ts文件
.ts文件:
既包含类型信息又可执行代码;- 可以被编译为.js文件,然后,执行代码;
- 用途:编写可执行的程序代码;
.d.ts文件:
只包含类型信息的类型声明文件;不会生成.js文件,仅用于提供类型信息;- 用途:为js提供类型信息;
总结:.ts是implementation(代码实现文件);.d.ts是declaration(类型声明文件);
4.2 类型声明文件的使用说明
- 创建自己的类型声明文件:1.
项目内共享类型2.为已有JS文件提供类型声明
- 项目内共享类型:如果
多个.ts文件中都用到同一个类型,此时可以创建.d.ts文件提供该类型,实现类型共享。
// index.d.ts
type Props = { x: number, y: number };
export {
Props
}
// a.ts
import { Props } from './index';
let p1: Props = {
x: 1,
y: 2,
}
// b.ts
import { Props } from './index';
let p2: Props = {
x: 3,
y: 4,
}
- 为已有JS文件提供类型声明:在
将js项目迁移到ts项目时,为了让已有的.js文件有类型声明。
// utils.js
let count = 10;
let songName = '探故知';
let position = {
x: 0,
y: 0
}
function add(x, y) {
return x + y;
}
function changeDirection(direction) {
console.log(direction);
}
const fomartPoint = point => {
console.log(point);
}
export default {
count,
songName,
position,
add,
}
// utils.d.ts
declare let count: number;
declare let songName: string;
interface Point {
x: number,
y: number
};
declare let position: Point;
declare function add(x: number, y: number): number
declare function changeDirection(direction: 'up' | 'down' | 'left' | 'right'): void;
type FomartPoint = (point: Point) => void;
declare const fomartPoint: FomartPoint;
// 注意:类型提供好之后,需要使用模块化方案中提供的模块化语法,来导出声明好的类型,然后才能在.ts文件中使用。
export {
count,
songName,
position,
add,
changeDirection,
fomartPoint
}
说明:在导入.js文件时,TS会自动加载与.js同名的.d.ts文件,以提供类型声明。
declare关键字:用于类型声明,以其他地方(比如,.js文件)已存在的变量声明类型,而不是创建一个新的变量。
- 对于type interface等这些明确就是TS类型的(只能在TS中使用的),可以省略declare关键字
- 对于let function等具有双重含义(在js,ts中都能使用),应该使用
declare关键字,明确指定此处用于类型声明。
5. 在React中使用Typescript
5.1 使用CRA创建支持TS的项目
命令: npx create-react-app 项目名称 --template typescript 相对于非TS项目,项目结构主要有以下三个变化:
- 项目根目录增加了
tsconfig.json配置文件:指定TS的编译选项(比如:编译时是否移除注释)。 - React组件的文件扩展名变为:*.tsx。
- src目录增加了react-app-env.d.ts:React项目默认的类型声明文件。
react-app-env.d.ts:React项目默认的类型声明文件。
三斜线指令:指定依赖的其他类型声明文件,types表示依赖的类型声明文件包的名称。
解释:告诉TS帮我们加载react-scripts这个包提供的类型声明。
react-scirpts的类型声明文件包含了两部分类型:
- react,react-dom,node的类型
- 图片,样式等模块的类型,以允许在代码中导入图片,svg等文件。
TS会自动加载该.d.ts文件,以提供类型声明(通过修改tsconfig.json中的include配置来验证)。表示会把src目录下所有的tsx文件交给TS来处理
5.2 TS配置文件tsconfig.json
tsconfig.json指定:项目文件和项目编译所需的配置项。
注意:ts的配置项非常多(100+),以CRA项目中的配置为例来学习。
- tsconfig.json文件所在目录为项目根目录(与package.json同级)
- tsconfig.json可以自动生成,命令:
tsc --init
// [tsconfig文档链接](https://www.typescriptlang.org/tsconfig)
{
// 编译选项
"compilerOptions": {
// 生成代码的语言版本
"target": "es5",
// 指定要包含在编译中的library
"lib": [
"dom",
"dom.iterable",
"esnext"
],
// 允许 ts 编辑js文件
"allowJs": true,
// 跳过声明文件的类型检查
"skipLibCheck": true,
// es 模块 互操作,屏蔽ESModule 和 CommonJs之间的差异
"esModuleInterop": true,
// 允许通过 import x from 'y' 即使模块没有显式指定default导出
"allowSyntheticDefaultImports": true,
// 是否开启严格模式
"strict": true,
// 对文件名强制区分大小写
"forceConsistentCasingInFileNames": true,
// 为Switch 语句启用错误报告
"noFallthroughCasesInSwitch": true,
// 生成代码的模块化标准
"module": "esnext",
// 模块解析(查找)策略
"moduleResolution": "node",
// 允许导入扩展名为.json模块
"resolveJsonModule": true,
// 是否将没有import/export的文件视为旧(全局而非模块化)脚本
"isolatedModules": true,
// 编译时不生成任何文件(只进行类型检查)
"noEmit": true,
// 指定将 jsx 编译成什么形式
"jsx": "react-jsx"
},
// 指定允许 ts 处理的目录
"include": [
"src"
]
}
5.3 React中常用类型
react是组件化开发模式,react开发主要任务就是写组件,两种组件:1. 函数组件 2. class组件
1.函数组件,主要包括以下内容:
- 组件的类型
- 组件的属性(props)
- 组件属性的默认值(defaultProps)
- 事件绑定和事件对象
函数组件的类型以及组件的属性
// 写法1:
import { FC } from 'react';
/**
* 函数组件
* 组件的类型
* 组件的属性(props),包括可选属性
*/
type Props = { uname: string; age?: number }
// FC 是react提供的泛型类型
const Hello: FC<Props> = ({ uname,age}) => (
<div>你好我叫:{uname} 今年:{ age}</div>
)
/**
* 写法2:(推荐)
* 说明:完全利用JS(TS) 自身的能力来编写组件
* 函数组件
* 组件的类型
* 组件的属性(props),包括可选属性
*/
type Props = { uname: string; age?: number }
const Hello = ({ uname,age}:Props) => (
<div>你好我叫:{uname} 今年:{ age}</div>
)
function App() {
return (
<div>
<Hello uname="rose" age={18}></Hello>
</div>
)
}
组件属性的默认值(defaultProps)
// 写法1:
type Props = { uname: string; age?: number }
const Hello = ({ uname,age}:Props) => (
<div>你好我叫:{uname} 今年:{ age}</div>
)
// 给Hello组件设置age默认值为20
// 我们如果传入了age则已传入的为准,反之取默认值
Hello.defaultProps = {
age:20
}
// 写法2:
type Props = { uname: string; age?: number }
// 直接在对象属性结构的时候为age赋值20为默认值,如果传递了age则已传递的为准
const Hello = ({ uname,age = 20 }:Props) => (
<div>你好我叫:{uname} 今年:{ age}</div>
)
function App() {
return (
<div>
<Hello uname="rose" age={16}></Hello>
</div>
)
}
事件绑定和事件对象
type Props = { uname: string; age?: number }
const Hello = ({ uname, age = 20 }: Props) => {
// 需要指定实践对象,这里才可以访问到e
const onclick = (e:React.MouseEvent<HTMLButtonElement>) => {
console.log('赞~',e.currentTarget)
}
// 需要指定实践对象,这里才可以访问到e
const onChange = (e:React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value,'e')
}
return (
<div>
<div>你好我叫:{uname} 今年:{age}</div>
<button onClick={onclick}>点赞</button>
<input placeholder='请输入' onChange={onChange}></input>
</div>
)
}
function App() {
return (
<div>
<Hello uname="rose" age={16}></Hello>
</div>
)
}
class组件的类型
type State = { count:number }
type Props = { message?: string }
class C1 extends React.Component {} // 无Props 无state
class C1 extends React.Component<Props> {} // 有Props,无state
class C1 extends React.Component<{},State> {} // 无Props,有state
class C1 extends React.Component<Props,State> {} // 有Props,有state
class组件的属性和属性默认值
/**
* class组件
* 组件类型
*/
type Props = { name: string, age?: number }
class Hello extends React.Component<Props> {
// 写法1:
static defaultProps: Partial<Props> = {
age:18
}
render () {
const { name,age } = this.props
return (
<div>你好我叫:{name },今年年龄{ age }了</div>
)
}
}
/**
* class组件
* 组件类型
*/
type Props = { name: string, age?: number }
class Hello extends React.Component<Props> {
// (推荐)写法2:
render () {
// 简化class组件的属性默认值
const { name,age = 20 } = this.props
return (
<div>你好我叫:{name },今年年龄{ age }了</div>
)
}
}
function App() {
return (
<div>
<Hello name='jack'></Hello>
</div>
)
}
class组件状态(state)和事件
// 定义类型
type State = { count: number }
// 提供第二个类型参数,方便在setState的时候有提示
class Counter extends React.Component<{}, State> {
state: State = {
count: 0
}
handleClick = () => {
this.setState({
count:this.state.count +1
})
}
render() {
return (
<div>
<span>计算机:{ this.state.count}</span>
<button onClick={this.handleClick}>+1</button>
</div>
)
}
}
function App() {
return (
<div>
<Counter></Counter>
</div>
)
}