持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
TypeScript
一、什么是TypeScript
以JavaScript为基础构建的语言,一个JavaScript的超集
可以在任何支持JavaScript的平台执行,TypeScript扩展了JavaScript并添加了类型
TS不能被JS解析器直接执行
TS必须编译成JS
安装Node.js
使用npm全局安装typescript
- 进入命令行
- 输入:
npm i -g typescript
创建一个ts文件
使用tsc对ts文件进行编译
- 进入命令行
- 进入ts文件所在目录
- 执行:
tsc xxx.ts编译,或者tsc xxx.ts -w监视
在项目下面加 tsconfig.json 文件直接执行 tsc -w 所有ts文件都会被编译
初始化项目步骤
- 使用
npm init -y初始化项目,生成package.json
npm i -D webpack webpack-cli typescript ts-loader-D开发环境 安装依赖
webpack 打包工具
webpack-cli 用于命令行调用webpack
typescript TypeScript编译器
ts-loader 用于 webpack 的 TypeScript 加载器(TypeScript loader for webpack)
- 新建
webpack.config.jswebpack配置文件
@babel/core
@babel/preset-env
babel-loader
core-js
在React中使用TypeScript
1.使用CRA创建支持TS项目
React脚手架工具create-react-app(简称:CRA) 默认支持TypeScript
创建支持TS的项目命令:npx create-react-app 项目名称 --template typescript
给已有React项目配置ts,看creat-react-app官方文档
项目结构:
react-app-env.d.ts: React项目默认的类型声明文件
三斜线指令:指定依赖的其他类型声明文件,types表示依赖的类型声明文件包的名称
解释:告诉TS帮我加载react-scripts 这个包提供的类型声明
react-scripts 的类型声明文件包含了两部分类型:
- react、react-dom、node 的类型
- 图片、样式等模块的类型,以允许在代码中导入图片、SVG等文件
TS会自动加载该 .d.ts 文件,以提供类型声明(通过修改tsconfig.json中的include配置来验证)
2.TS配置文件tsconfig.json
3.React中的常用类型
函数组件
组件的类型:
//两种写法都可以,第二种更简单 //加上组件类型 const Hello: FC<Props> = ({ name, age }) => ( <div>名字:{name},年龄:{age}</div> ) //完全按照TS中函数写法来写组件 const HelloWorld = ({ name, age }: Props) => ( <div>名字:{name},年龄:{age}</div> )组件属性的默认值:
//属性默认值,两种方法 const Hello: FC<Props> = ({ name, age }) => ( <div>名字:{name},年龄:{age}</div> ) // defaultProps是可选版本的Props Hello.defaultProps = { age: 19, name: '默认' } const HelloWorld = ({ name, age = 90 }: Props) => ( <div>名字:{name},年龄:{age}</div> )事件绑定和事件对象
const onClick = (e: React.MouseEvent<HTMLButtonElement>) => { console.log(e.currentTarget); console.log('hhhh'); }
class组件
//* 无props无state class C1 extends React.Component { } //* 有props无state class C2 extends React.Component<Props> { } //* 无props有state class C3 extends React.Component<{}, State> { } //* 有props有state class C4 extends React.Component<Props, State> { }组件属性
type Props = { name: string; age?: number } class ClassHello extends React.Component<Props>{ //props属性设置默认值 static静态属性 只能用类本身访问 static defaultProps: Partial<Props> = { age: 99, } render() { //也可以解构时赋值,两种赋值 const { name, age = 19 } = this.props return ( <div>nihao name: {name} age: {age}</div> ) } }事件绑定
type State = { counter: number } class ClassHello extends React.Component<Props, State>{ state: State = { counter: 0 } //this.setState第一个参数可传函数 handleClick = () => { this.setState((preState, props) => ({ counter: preState.counter + 1 })) } render() { return ( <> <button onClick={this.handleClick}> +1 </button> 计数器:{this.state.counter} </> ) } }
二、基本类型
类型声明
声明变量a 指定为number类型
let a: number;
a = 10;
a = 'hello';//报错
声明完变量直接进行赋值
let c: boolean = false
声明变量同时赋值,TS自动对变量进行类型检测
let d = false
d = 123;//报错
声明变量不指定类型,TS自动判断为any
let e;
类型断言 告诉编译器e变量是string类型
语法:
变量 as 类型
<类型>变量
s = e as string
s = <string>e
指定对象中包含哪些属性,语法:{属性名:属性值}
age?:表明此属性可有可无,[propName: string]: any 可添加任意属性
let obj: { name: string, age?: number, [propName: string]: any }
设置函数类型声明
语法:(形参:类型,形参:类型) => 返回值
let fun: (a: number, b: number) => number
function sum(a:number,b:number):number{
return a+b;
}
const sum = (a:number,b:number):number => {
return a+b;
}
//这里void表示没有返回值
const sum = (a:number,b:number):void => {
console.log(a,b)
}
//?表示可选参数
function sum(a?:number,b?:number):number{
return a+b;
}
数组类型声明
let numbers :number[] =[1,3,5]
let arr: string[];
let Arr: Array<number> = ['a','b','c']
let arr1 :(number | string)[] = [1,'1',2,'3']
元组
固定长度数组,语法:[类型,类型]
let asd: [number, number] =[1,2]
字面量类型
let direction: 'up' | 'down' | 'left' | 'right'
enum 枚举
enum Gender {
Male,
Female,
}
let i: { name: string, gender: Gender }
i = {
name: 'qqq',
gender: Gender.Male,
}
//枚举成员有值,默认: 从0开始自增
enum Direction {
Up = 'up',
Down = 'down',
Left = 'left',
Right = 'right',
}
//字符串枚举没有自增长行为,因此,字符串枚举每个成员必须有初始值
| 联合类型,由两个或多个其他类型组成的类型,表示可以是这些类型中的任意一种
let arr1 :(number | string)[] = [1,'1',2,3]
fn: (value: string | number) => string
& 交叉类型,用于组合多个类型为一个类型
let j: { name: string } & { age: number };
j = {
name: '1111',
age: 1
}
interface Person1 {
name: string
}
interface Contact {
age: number
}
type PersonDetail = Person1 & Contact
let obj: PersonDetail = {
name: 'ja',
age: 17,
}
interface A {
fn: (value: number) => string
}
interface B {
fn: (value: string) => string
}
type C = A & B
类型别名
type myType = 1 | 2 | 3 | 4 | 5
let k: myType;
let l: myType;
typeof可以获取类型上下文的类型
let p = { x: 1, y: 2 }
let a: typeof p
//相当于
let a:{
x:number;
y:number;
}
//只能查询变量或者对象属性的类型,不能查询函数调用的类型
类型
| 类型 | 描述 | 备注 |
|---|---|---|
| number | 任意数字 | |
| string | 任意字符串 | |
| boolean | 布尔值true false | |
| 字面量 | 限制变量的值就是该字面量的值 | |
| any | 任意类型 | 声明变量不指定类型,TS自动判断为any ,any类型可赋值给任意变量 |
| unknown | 类型安全的any | 不可赋值给任意变量 |
| void | 表示空,没有值(或undefined) | 函数就是没有返回值 |
| never | 不能是任何值 | 函数:永远不会返回结果 |
| objet | 任意的JS对象 | |
| array | 任意的JS数组 | |
| tuple | 元组、TS新增类型,固定长度数组 | 固定长度的数组 |
| enum | 枚举,TS新增类型 |
抽象类
(function () {
/**
* 以abstract开头的类是抽象类
* 抽象类只是不能用来创建对象
* 专门用来继承
*/
abstract class Animal {
name: string
constructor(name: string) {
this.name = name
}
/**
* 定义抽象方法
* 以abstract开头,没有方法体
* 抽象方法只能定义在抽象类中,子类必须对抽象方法进行重写
*/
abstract sayHello(): void
}
class Dog extends Animal {
sayHello(): void {
console.log('汪汪汪汪');
}
}
const dog = new Dog('狗');
dog.sayHello()
})();
接口
(function () {
//描述一个对象类型
type myType = {
name: string,
age: number
}
//接口用来定义一个类结构
/**
* 接口用来定义类,限制类的结构
* 接口只定义对象的结构,不能有实际值
*接口中所有方法都是抽象方法
*/
interface myInterface {
name: string,
age: number,
}
interface myInterface {
gender: string
}
const obj: myInterface = {
name: 'aa',
age: 19,
gender: 'man',
}
//接口继承
interface Point2D { x: number; y: number }
interface Point3D extends Point2D { z: number }
let p3: Point3D = {
x: 1,
y: 2,
z: 2
}
})()
实现接口
interface Singable {
sing(): void
}
class Person implements Singable {
sing(): void {
console.log('aaaa1');
}
}
属性封装 & 成员可见性
(function () {
class Person {
//TS可以在属性前添加属性的修饰符
/**
* 不写的时候默认public
* public 修饰的属性可以在任意位置访问(修改)
* private 私有属性,只能在类内部进行访问书写
* protected 受保护属性,允许在类或子类中访问
* readonly 表示只读,用来防止在构造函数之外对属性进行赋值
*/
/**
private _name: string
private _age: number
constructor(name: string, age: number) {
this._name = name
this._age = age
}
*/
constructor(private name: string, private age: number) {
this._name = name
this._age = age
}
get name() {
return this._name
}
set name(name: string) {
this._name = name
}
}
const per = new Person('aa', 18)
console.log(per.name);
})()
泛型
// 定义函数或类,遇到不明确就可以使用泛型
function fn<T>(a: T): T {
return a
}
//可以直接调用具有泛型的函数
let result = fn(10); //不指定泛型,TS可以自动对类型进行推断
let result2 = fn<string>('hello')//指定泛型
//泛型可以同时指定多个
function fn2<T, K>(a: T, b: K) {
return a
}
fn2<number, string>(1, '1')
class MyClass<T>{
name: T;
constructor(name: T) {
this.name = name
}
}
const mc = new MyClass<string>('111')
泛型约束
//1.指定更加具体的类型
function id<Type>(value:Type[]):Type{
value.length
}
//比如将类型修改为Type[](Type类型的数组)、
//2.添加约束
interface Inter {
length: number;
}
//T extends Inter表示泛型T必须是Inter的子类
function fn3<T extends Inter>(a: T): number {
return a.length
}
//T类型必须有length属性
// 类型变量间可以相互约束
function getProp<Type, Key extends keyof Type>(obj: Type, key: Key) {
return obj[key]
}
let person = { name: 'Jack', age: 28 }
getProp(person, 'name')
/**
* 1.keyof关键字接收一个对象类型,生成其键名称(字符串或数字)的联合类型
* 2.这里keyof Type 获取到的是person对象的所有键的联合类型,也就是'name'|'age'
* 3.Key受Type约束,Key只能是Type所有键中的任意一个,只能访问对象中存在的属性
*/
泛型接口
JS中的数组在TS中就是一个泛型接口
使用数组时,TS会根据不同的数组类型,来自动将类型变量设置为响应的类型
interface Array<T> {
forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;
}
泛型类
class GenericNumber<NumType>{
defaultValue: NumType
add!: (x: NumType, y: NumType) => NumType;
constructor(value: NumType) {
this.defaultValue = value
}
}
const myNum = new GenericNumber(100)
泛型工具类型
interface Props {
id: string,
title: string,
children: number[],
}
//Partial<Type>用来创建一个类型,将Type的所有属性设置成可选
//新构造出来的PartialProps 跟 Props相同,但所有属性都变成可选的
type PartialProps = Partial<Props>
//Readonly<Type>用来创建一个类型,将Type的所有属性设置成只读
type ReadonlyProps = Readonly<Props>
//Pick<Type>用来创建一个类型,选择出Type中一部分属性
type PickProps = Pick<Props, 'id' | 'title'>
//Record<Keys,Type>构造一个对象类型,属性键为Keys,属性类型Type
type RecordProps = Record<'a' | 'b', string[]>
索引签名类型
//只要键名是字符串就可以添加进对象
interface AnyObject {
[key: string]: number
}
let obaj: AnyObject = {
a: 1,
ada: 90,
fhaudfh: 1000,
}
//数组也是用了索引签名
[n:number]:T
映射类型
//基于旧类型创建新类型(对象类型)Key in PropKeys表示key可以是PropKeys中的任一个
//映射类型只能在类型别名中使用,不能在接口中使用
type PropKeys = 'x' | 'y' | 'z' | 'ada'
type Type1 = { [Key in PropKeys]: number }
//也可以根据对象类型来创建 keyof 拿到对象中所有key
type Type2 = { [key in keyof Type1]: string }
//索引查询类型 可以拿到Type1里面的 x 属性的类型 []里面只要传入key就可以
type TypeA = Type1['x']
type TypeB = Props[keyof Props]
type TypeC = Props['id' | 'children']
类型兼容性 & 接口的兼容性 (一样) & 类与接口之间也是兼容的
//两个类属性相同类型相同,就会发生类型兼容,成员多的也可以赋值给少的
class Point {
x: number = 1
y: number = 2
}
class Point2D {
x: number = 3
y: number = 4
}
class Point3D {
x: number = 3
y: number = 4
z: number = 5
}
const pap: Point = new Point2D()
函数参数兼容性: 参数少的可以赋值给参数多的;
类型声明文件
为已有的 JS 文件提供类型声明
在导入 .js 文件时TS会自动加载与.js同名的.d.ts文件,以提供类型声明
declare 关键字:用于类型声明,为其他地方已存在的变量声明类型,而不是创建一个新的变量
- 对于type,interface这种明确就是TS类型的(只能在TS中使用)可省略declare关键字
- 对于let function等在 TS 和 JS 中都能使用的,应该用declare关键字,明确指定此处用于类型声明