Setting things up
让我们开始编写我们的第一个TypeScript应用,为了保持简单,让我们通过使用ts-node这个 npm 包开始。
它能立即编译并执行指定的 TypeScript 文件,因此不需要单独的编译步骤。
在空目录中运行命令**npm init**
来设置 npm 项目
安装ts-node和typescript依赖
npm install --save-dev ts-node typescript
在package.json中设置scripts
{
// ..
"scripts": {
"ts-node": "ts-node"
},
// ..
}
在package.json中使用ts-node, 需要添加前缀 —
npm run ts-node -- file.ts
TypeScript 还提供了一个在线playground, 可以用来编写和测试TS代码
Creating your first own types
联合类型 union type
可以返回几个指定的值,类型可以为string | number, | 表示或
type Operation = 'multiply' | 'add' | 'divide'
计算器程序代码:
type Operation = 'multiply' | 'add' | 'divide'
type Result = number | string
const calculator = (a: number, b: number, op: Operation): Result => {
switch (op) {
case 'multiply':
return a * b
case 'divide':
if (b === 0) throw new Error("Can't divide by 0!")
return a / b
case 'add':
return a + b
default:
throw new Error('Operation is not multiply, add or divide!')
}
}
try {
console.log(calculator(1, 5, 'divide'))
} catch (e) {
console.log('Something went wrong, error message: ', e.messsage)
}
可以通过安装一个名为@types /-prefix 的 npm 包将相关类型添加到项目中。 例如: npm install —save-dev @types/react @types/express @types/lodash @types/jest @types/mongoose 等等。
通过安装包 @types/node 获得Node类型
npm install --save-dev @types/node
安装node类型后,cannot find name ‘console’的错误就消失了
修复错误:error的类型为unknown
加if判断
try {
console.log(calculator(1, 5, 'divide'))
} catch (e) {
if (e instanceof Error)
console.log('Something went wrong, error message: ', e.message)
}
Improving the project
改进程序,让程序可以从命令行获取参数process.argv
const multiplicator = (a: number, b: number, printText: string) => {
console.log(printText, a * b)
}
const a: number = Number(process.argv[2])
const b: number = Number(process.argv[3])
multiplicator(a, b, `Multiplied numbers ${a} and ${b}, the result is:`)
在script中添加multiply
"scripts": {
"ts-node": "ts-node",
"multiply": "ts-node multiplier.ts",
// ...
},
执行命令**npm run multiply 5 2**
, 则能获取到参数5和2
但是当参数不为数字时,得到NaN, 修改代码来避免这个bug
interface MultiplyValues {
value1: number
value2: number
}
const parseArguments = (args: Array<string>): MultiplyValues => {
if (args.length < 4) throw new Error('Not enough arguments')
if (args.length > 4) throw new Error('Too many arguments')
if (!isNaN(Number(args[2])) && !isNaN(Number(args[3]))) {
return {
value1: Number(args[2]),
value2: Number(args[3]),
}
} else {
throw new Error('Provided values were not numbers!')
}
}
const multiplicator = (a: number, b: number, printText: string) => {
console.log(printText, a * b)
}
try {
const { value1, value2 } = parseArguments(process.argv)
multiplicator(
value1,
value2,
`Multiplied ${value1} and ${value2}, the result is: `
)
} catch (e) {
if (e instanceof Error)
console.log('Error, something bad happened, message: ', e.message)
}
**args: Array<string>**
表示参数的类型是字符串数组
返回值类型MultiplyValues用关键字interface声明,interface用来定义对象应该具有的形状(shape)
Exercises 9.1.-9.3.
在一个库目录**npm init**
创建项目
安装ts-node和typescript
npm install --save-dev ts-node typescript
新建tsconfig.json文件, 添加下列配置,使所有变量都必须有类型
{
"compilerOptions": {
"noImplicitAny": true,
}
}
exercise 9.2
写一个程序,计算运动是否达标
interface Result {
periodLength: number
trainingDays: number
target: number
average: number
success: boolean
rating: number
ratingDescription: string
}
const calculateExercises = (
trainingTime: Array<number>,
target: number
): Result => {
const periodLength = trainingTime.length
const trainingDays = trainingTime.filter((i) => i !== 0).length
const average =
trainingTime.reduce((sum, item) => sum + item, 0) / periodLength
const success = average >= target ? true : false
let rating = 0,
ratingDescription = ''
if (average < 0.5) {
rating = 1
ratingDescription = 'keep going'
} else if (average >= 0.5 && average < 1) {
rating = 2
ratingDescription = 'not too bad but could be better'
} else {
rating = 3
ratingDescription = 'awesome!'
}
return {
periodLength,
trainingDays,
success,
rating,
ratingDescription,
target,
average,
}
}
console.log(calculateExercises([3, 0, 2, 4.5, 0, 3, 1], 2))
More about tsconfig
文件tsconfig.json包含您希望 TypeScript 如何在项目中工作的所有核心配置。
在tsconfig.json 文件中指定如下配置:
{
"complierOptions": {
"target": "ES2020",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"esModuleInterop": true
}
}
Adding express to the mix
安装express
npm install express
在scripts中添加start
{
// ..
"scripts": {
"ts-node": "ts-node",
"multiply": "ts-node multiplier.ts",
"calculate": "ts-node calculator.ts",
"start": "ts-node index.ts"
},
// ..
}
新建index.ts
const express = require('express')
const app = express()
app.get('/ping', (req, res) => {
res.send('pong')
})
const PORT = 3003
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`)
})
vscode提示可以将require改成import
如果点击Quick Fix,会自动修改成
import express = require('express')
改成import后提示安装 @types/express
npm i --save-dev @types/express
如果使用const … require的方式引入express, 编辑器不知道参数的类型
但是如果使用import,编辑器知道参数的类型
所以优先使用import
如果**import...from...**
不可以使用,则使用**import...=require("...")**
的混合形式
vscode提示有未使用的参数
将配置中的noUnusedParameters改成false
"noUnusedParameters": false,
点击Quick Fix, 给未使用的参数加上下划线
app.get('/ping', (_req, res) => {
res.send('pong')
})
npm run start 启动应用
安装ts-node-dev,让应用自动重载
npm i --save-dev ts-node-dev
在scripts中添加dev
{
// ...
"scripts": {
// ...
"dev": "ts-node-dev index.ts",
},
// ...
}
运行npm run dev
, 程序就能自动重载
Exercise 9.4-9.5
GET 请求和使用[查询字符串参数][query string parameters]
当get请求为http://localhost:3002/bmi?height=180&weight=72时,
使用req.query获取传递的参数
import express = require('express')
import calculateBmi from './bmiCalculator'
const app = express()
app.get('/bmi', (req, res) => {
res.send(calculateBmi(Number(req.query.height), Number(req.query.weight)))
})
const PORT = 3003
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`)
})
The horrors of any
在TypeScript中,每个不能被推断的未被定义类型的变量都变成隐式的any类型。 Any 是一种“通配符wildcard”类型,字面上代表whatever type。
隐式any 类型通常被认为是有问题的
这就是为什么配置规则noImplicitAny,让其存在于编译器级别,并且强烈建议在任何时候都保持开启状态。
我们已经有了noImplicitAny 配置,那么为什么编译器不产生警告隐含any 类型呢?
原因是,express Request对象的query 字段是显式给定any类型的。 我们用于将数据发布到应用的request.body 字段也是如此。
除了tsconfig.json, 我们可以使用ESLint来管理我们的代码
安装
npm install --save-dev eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser
使用命令 **node_modules/.bin/eslint --init**
初始化eslint规则
在.eslintrc中添加规则
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking"
],
"plugins": ["@typescript-eslint"],
"env": {
"node": true,
"es6": true
},
"rules": {
"@typescript-eslint/semi": ["error"],
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/restrict-plus-operands": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{ "argsIgnorePattern": "^_" }
],
"no-case-declarations": "off"
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
}
}
scripts中添加脚本
"scripts": {
// ...
"lint": "eslint --ext .ts .",
},
可以通过添加注释取消对any类型的检查
app.post('/calculate', (req, res) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { value1, value2, op } = req.body
const result = calculator(value1, value2, op)
res.send(result)
})