TypeScript 笔记
今天是我参与「第四届青训营 」笔记创作活动的第4天。
记录了typescript的一些基本使用。
介绍
2012年微软发布了TypeScript项目,Anders Hejlsberg 负责开发TS项目,这个项目是开源的,并且对标ES开发标准。
官网:传送门
中文网:传送门
一、 为什么要用TypeScript
1. JS语言的问题
使用JS经常遇到的问题:
- 使用了不存在的变量、属性或成员。
- 把一个不确定的类型当做一个确定的类型进行处理。
- 使用了
null或undefined的成员。
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 ,为的是获得最大的兼容性。
更改默认的假设,用两种方式:
- 使用ts配置文件,更改编译选项。
- 使用
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[];orlet nums: Array<number>; -
object:对象 -
null和undefined:空类型- 是所有其他类型的子类型。可以赋值给其他类型。
- 可以通过
"strictNullChecks":true配置tsconfig.json文件,获得更严格的类型检查,使null和undefined只能赋值给自身。 - 如果一个变量可能是
string或undefined,可以通过联合类型: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] -
any:any类型可以绕过类型检查,因此,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>
)
}
}
\