初见TypeScript及其基本使用

129 阅读10分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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文件都会被编译

初始化项目步骤

  1. 使用 npm init -y 初始化项目,生成package.json
  1. npm i -D webpack webpack-cli typescript ts-loader -D开发环境 安装依赖

webpack 打包工具

webpack-cli 用于命令行调用webpack

typescript TypeScript编译器

ts-loader 用于 webpack 的 TypeScript 加载器(TypeScript loader for webpack)

  1. 新建webpack.config.js webpack配置文件

@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表示依赖的类型声明文件包的名称

三斜线指令.png 解释:告诉TS帮我加载react-scripts 这个包提供的类型声明

react-scripts 的类型声明文件包含了两部分类型:

  1. react、react-dom、node 的类型
  2. 图片、样式等模块的类型,以允许在代码中导入图片、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');
 }

查看事件对象类型.png

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 关键字:用于类型声明,为其他地方已存在的变量声明类型,而不是创建一个新的变量

  1. 对于type,interface这种明确就是TS类型的(只能在TS中使用)可省略declare关键字
  2. 对于let function等在 TS 和 JS 中都能使用的,应该用declare关键字,明确指定此处用于类型声明