TypeScript基本使用 | 青训营笔记

48 阅读6分钟

TypeScript 笔记

今天是我参与「第四届青训营 」笔记创作活动的第4天。

记录了typescript的一些基本使用。

介绍

2012年微软发布了TypeScript项目,Anders Hejlsberg 负责开发TS项目,这个项目是开源的,并且对标ES开发标准。

官网:传送门

中文网:传送门

一、 为什么要用TypeScript

1. JS语言的问题

使用JS经常遇到的问题:

  • 使用了不存在的变量、属性或成员。
  • 把一个不确定的类型当做一个确定的类型进行处理。
  • 使用了nullundefined 的成员。

js本身的问题:

  • js语言本身的特征,决定了该语言无法适应大型的复杂的项目。
  • 弱类型:某个变量,可以随时更改类型。
  • 解释型:错误发生的时间,是在运行时,不利于快速发现问题。

因此,在JS的前端开发中,大部分的时间都是在排错。

2. TS语言的特点

TypeScript是JS的超集,是一个可选的、静态的类型系统。

  • 类型系统:对代码中所有的标识符(变量、函数、参数、返回值)进行类型检查。
  • 可选的:可以完全使用JS代码,学习曲线非常平滑。
  • 静态的:在编译的时候,而非运行时,就进行了类型检查。TS在运行时不再参与任何类型检查。

无论是浏览器环境还是node环境,都无法直接识别 ts代码。

tsc:ts编译器。

babel:es6 -> es5

tsc:ts -> es

二、搭建TS开发环境

1. 安装TypeScript

yarn global add typescript

安装 typescript 后会自动添加 tsc命令行工具。

默认情况下,TS会作出几种假设:

  • 假设当前的执行环境是浏览器环境。
  • 如果代码中没有使用模块化语句(import、export),会认为该代码是全局运行的。
  • 编译的目标代码是ES3 ,为的是获得最大的兼容性。

更改默认的假设,用两种方式:

  1. 使用ts配置文件,更改编译选项。
  2. 使用tsc命令行的时候,加上选项参数(不常用)。

2. 配置TypeScript

使用 tsc --init命令生成 tsconfig.json配置文件。

使用了配置文件后,使用 tsc命令行进行编译时,不能跟上文件名。如果跟上文件名,会忽略配置文件。

TypeScript默认是没有node环境的,需要安装@types/node 模块。

@types是一个ts官方的类型库,其中包含了很多对js代码的类型描述。

jQuery:用js写的,没有类型检查。

安装@types/jquery,为jQuery库添加类型定义。

{
  "compilerOptions": { //编译选项
    "target": "ES2016", //配置编译目标代码的版本标准
    "module": "commonjs", //配置编译目标使用的模块化标准
    "lib": ["ES2016"], //配置当前默认的环境
    "outDir": "./dist", //配置编译后js代码的输出位置
     "strictNullChecks": true, //更加严格的空类型检查
  },
  "include": ["./src"] //配置哪些目录下的ts代码进行编译
}

3. 使用第三方库简化流程

  • ts-node:将ts代码在内存中完成编译,同时完成运行。使用yarn global add ts-node全局安装。

  • nodemon:主要用于检测文件变化,使用命令nodemon --exec ts-node src/index.ts

    • 或者配置到package.json中,"scripts": { "dev": "nodemon --watch src -e ts --exec ts-node src/index.ts"}
    • nodemon配置中的--watch src-e ts字段代表:只监听src目录下的ts文件变化。

三、类型约束

TypeScript是JS的超集,是一个可选的、静态的类型系统。

如何进行类型约束?

  • 只要在 变量、函数的参数,函数的返回值位置上加上:类型
  • any:表示任意类型,对该类型,ts不进行类型检查。
  • ts在很多场景可以完成类型推导。比如:let name = "杜金超",就可以自己推导出name的类型为string

如何区分数字字符串和数字的类型?

看怎么读,如果按数字的方式阅读,则为数字;否则为字符串。比如:手机号就是字符串。

let name:string;
name = "杜金超";
const isOld(n: number): boolean{
    return n%2 === 0;
}

1. 基本类型

  • number:数字类型

  • string:字符串

  • boolean:布尔

  • 数组:let nums: number[]; or let nums: Array<number>;

  • object:对象

  • nullundefined:空类型

    • 是所有其他类型的子类型。可以赋值给其他类型。
    • 可以通过"strictNullChecks":true 配置tsconfig.json文件,获得更严格的类型检查,使nullundefined只能赋值给自身。
    • 如果一个变量可能是stringundefined,可以通过联合类型:let name: string | undefined;

2. 其他常用类型

  • 联合类型:多种类型任选其一。可以配合类型保护进行判断,如:typeof

  • void:通常用于约束函数的返回值,表示没有返回值。

  • never:通常用于约束函数的返回值,表示该函数永远不会结束。

    • function throwError(msg: string): never{
          throw new Error(msg);
      }
      function something(): never{
          while(true){
              //...
          }
      }
      
  • 字面量类型:只能赋值给定的字面量。

    • let gender: "男" | "女";
    • let arr:[]; 表示arr只能取值为空数组。
    • let user: { name: string, age: number}
  • 元组类型(Tuple):一个固定长度的数组,并且数组中的每一项类型确定。

    let tu:[string, number]

  • anyany类型可以绕过类型检查,因此,any类型的数据可以赋值给任意类型。

3. 类型别名

对已知的一些类型定义名称。

格式:

type 类型名称 = ...

例子:

type Gender = "男"|"女"
type User = {
    name: string,
    age: number,
    gender: Gender
}
let u:User;
function getUsers(gender: Gender):User[]{
    //...
    return [...]
}

4. 函数相关约束

  • 函数重载:在函数实现之前,对函数调用的多种情况进行声明。

    • function combine(a:number,b:number):number; //- 得到a*b的结果
      function combine(a:string,b:string):string; //- 得到a+b的结果
      function combine(a:number|string,b:number|string):number|string{
          if(typeof a === "number" && typeof b === "number"){
              return a*b;
          }else if(typeof a === "string" && typeof b === "string"){
              return a+b;
          }else{
              return new Error("传入类型必须全为数字或字符串")
          }
      }
      const result = combine(1,2)
      
  • 可选参数:可以将传递的参数设定为可选的。

    • function sum(a: number,b: number, c?: number){
          if(c){
              return a + b + c;
          }
          return a + b;
      }
      sum(1,2);
      sum(1,2,3)
      
    • 默认参数默认是可选的。
    • 可选参数必须放在参数列表的末尾。
  • 默认参数:

    • function getNumber(n:number = 0){
      	return n;
      }
      

React中使用TS

创建项目:

yarn create react-app my-app --typescript

1. React + JS 遇到的问题

  • 某个组件有哪些属性需要传递?
  • 属性传递需要什么数据类型?
  • 传递事件时,有哪些参数?
  • 错误发生在运行时,效率太低。

可以通过propTypes约束属性类型,但是发生错误的时间是在运行时,效率太低。

2. React 组件书写方式

展示组件:通常是函数式组件。

容器组件:通常是类组件。

函数组件

interface IProps {
    num: number,
    onChange?: (n: number) => void
}

//export function CountComp(props: IProps){   //- 方式一
export const CountComp: React.FC<IProps> = (props) =>{	//- 方式二
    return (
    	<div>
        	<button onClick={()=>{
                    if(props.onChange){
                        props.onChange(props.num - 1);
                    }
                }}>-</button>
            <span>{props.num}</span>
        	<button onClick={()=>{
                    if(props.onChange){
                        props.onChange(props.num + 1);
                    }
                }}>+</button>
        </div>
    )
}

类组件

interface IProps {
    num: number,
    onChange?: (n: number) => void
}

interface IState {
    msg: string,
    description: string
}

export class CountComp extends React.Component<IProps>{
    state: IState = {
        msg: "",
        description: ""
    }
    render() {
        return (
            <div>
                <button onClick={()=>{
                        if(props.onChange){
                            props.onChange(props.num - 1);
                        }
                    }}>-</button>
                <span>{props.num}</span>
                <button onClick={()=>{
                        if(props.onChange){
                            props.onChange(props.num + 1);
                        }
                    }}>+</button>
            </div>
        )
    }
}

\