TypeScript 那些事儿 01 变量类型 😊 let 变量名 : 变量类型 = valuefunction ( 变量名:变量类型 ): 变量类型{}
数据类型
类型
例子
描述
number
1,22,2.5
任意数字
string
‘DuoR’,”bbb”
任意字符串
boolean
true,false
字面量
其本身
限制变量的值就是该字面量的值
any
*
任意类型
unknown
*
类型安全的 any
void
空值 undefined
没有值
never
没有值
不能是任何值
object
对象值
js Object
array
[1,2,3,]
js array
tuple
[4,5]
元组,表示一个已知元素数量和类型的数组
enum
enum{A,B}
枚举
联合声明
boolean | string
可以是 boolean 和 string 类型
变量类型之间是不可以互相赋值的,否则会报错
声明和赋值是同时进行的,TS 可以自动对类型进行检测,可以省略定义类型的步骤
类型断言,变量1 = <变量类型> 变量名2 或 变量1 = 变量名2 as 变量类型
类型的别名, type 自定义类型名 = 变量类型(|,&)
typescrpt 3.4 引入 readonly, 修饰后,变量只读:
const arr : readonly string[]= ['1' ,'2' ,'3' ]
注意点:
a. 在 tsconfig.json
指定了"strictNullChecks":false
,undefined
与 null
是可以赋值到其他类型的(string/num…),否则,null
和 undefined
只能赋值给 void
和它们各自的类型。
b. number
和 bigint
都是表示数字,但是这两个类相互不兼容的
函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 function sum (num1: number , num2: number ): number { return num1 + num2 }sum (98 , 29 )let sub = (num1 : number , num2 : number ): number => { return num1 - num2 }sub (98 , 29 )type Types = number | string function add (a: string , b: string ): string function add (a: number , b: number ): number function add (a: number , b: string ): string function add (a: string , b: number ): string function add (a: Types, b: Types ) { if (typeof a === 'string' || typeof b === 'string' ) { return a.toString () + b.toString () } return a + b }add ('1' , '2' ).split ('' )
特别的类型 never
表示的是那些永不存在的值的类型
两种情况:
function err (msg: string ): never { throw new Error (msg) }function loopForever ( ): never { while (true ) {} }
特别用法:
type Foo = string | number function controlFlowAnalysisWithNever (foo: Foo ) { if (typeof foo === 'string' ) { } else if (typeof foo === 'number' ) { } else { const check : never = foo } }type Foo = string | number | boolean
unknown
unknown与
any的最大区别是: 任何类型的值可以赋值给
any,同时
any类型的值也可以赋值给任何类型。
unknown 任何类型的值都可以赋值给它,但它只能赋值给
unknown和
any
function getDogName ( ) { let x : unknown return x }const dogName = getDogName ()const upName = dogName.toLowerCase () if (typeof dogName === 'string' ) { const upName = dogName.toLowerCase () }const upName = (dogName as string ).toLowerCase ()
Number、String、Boolean、Symbol
首先,我们来回顾一下初学 TypeScript 时,很容易和原始类型 number、string、boolean、symbol 混淆的首字母大写的 Number、String、Boolean、Symbol 类型,后者是相应原始类型的包装对象
,姑且把它们称之为对象类型。
从类型兼容性上看,原始类型兼容对应的对象类型,反过来对象类型不兼容对应的原始类型。
下面我们看一个具体的示例:
let num : number let Num : Number Num = num num = Num
此,我们需要铭记不要使用对象类型来注解值的类型,因为这没有任何意义。
object、Object 和 {}
另外,object(首字母小写,以下称“小 object”)、Object(首字母大写,以下称“大 Object”)和 {}(以下称“空对象”)
小 object 代表的是所有非原始类型,也就是说我们不能把 number、string、boolean、symbol 等 原始类型赋值给 object。在严格模式下,null
和 undefined
类型也不能赋给 object。
let lowerCaseObject : object lowerCaseObject = 1 lowerCaseObject = 'a' lowerCaseObject = true lowerCaseObject = null lowerCaseObject = undefined lowerCaseObject = {}
大 Object 代表所有拥有 toString、hasOwnProperty 方法的类型,所以所有原始类型、非原始类型都可以赋给 Object。同样,在严格模式下,null 和 undefined 类型也不能赋给 Object。
let upperCaseObject : Object upperCaseObject = 1 upperCaseObject = 'a' upperCaseObject = true upperCaseObject = null upperCaseObject = undefined upperCaseObject = {}
综上结论:{}、大 Object 是比小 object 更宽泛的类型(least specific),{} 和大 Object 可以互相代替,用来表示原始类型(null、undefined 除外)和非原始类型;而小 object 则表示非原始类型。
字面量
在 TypeScript 中,字面量不仅可以表示值,还可以表示类型,即所谓的字面量类型。
let specifiedStr : 'this is string' = 'this is string' let specifiedNum : 1 = 1 let specifiedBoolean : true = true
应用场景:
比如声明如下所示的一个类型 Config:
interface Config { size : 'small' | 'big' ; isEnable : true | false ; margin : 0 | 2 | 4 ; }
需要注意 :
在缺省类型注解的情况下,TypeScript 推断出它的类型直接由赋值字面量的类型决定
const str = 'this is string' const num = 1 const bool = true
02 ts 专业名词 🧐 类型推断
在很多情况下,TypeScript 会根据上下文环境自动推断出变量的类型,无须我们再写明类型注解。
let str : string = 'I am a string' let num : number = 1 let bool : boolean = true let str = 'I am a string' let num = 1 let bool = true
我们把 TypeScript 这种基于赋值表达式推断类型的能力称之为类型推断
在 TypeScript 中,具有初始化值的变量、有默认值的函数参数、函数返回的类型都可以根据上下文推断出来。比如我们能根据 return 语句推断函数返回的类型,如下代码所示:
function add1 (a: number , b: number ) { return a + b }const x1 = add1 (1 , 1 ) function add2 (a: number , b = 1 ) { return a + b }const x2 = add2 (1 )const x3 = add2 (1 , '1' )
但!如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查。
类型断言 TypeScript 类型检测无法做到绝对智能,毕竟程序不能像人一样思考。有时会碰到我们比 TypeScript 更清楚实际类型的情况,比如下面的例子:
const arrayNumber : number [] = [1 , 2 , 3 , 4 ]const greaterThan2 : number = arrayNumber.find ((num ) => num > 2 ) const arrayNumber : number [] = [1 , 2 , 3 , 4 ]const greaterThan2 : number = arrayNumber.find ((num ) => num > 2 ) as number
语法 let str : string = "我是string" let a : number = <number>str;let a : number = str as number;
非空断言 在上下文中当类型检查器无法断定类型时,可以使用 "!"
断言操作对象非 null 或 undefined
let str : null | undefined | string; str.toString () str!.toString () type Foo = () => numberfunction myFun (foo: Foo || undefined ) { foo (); foo!(); }
确定赋值断言 允许在实例属性和变量声明后面放置一个 "!"
号,从而告诉 TypeScript 该属性会被明确地赋值。为了更好地理解它的作用,我们来看个具体的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let x : number;initialize ();console .log (2 * x); function initialize ( ) { x = 10 ; }let x!: number;initialize ();console .log (2 * x); function initialize ( ) { x = 10 ; }
通过 let x!: number;
确定赋值断言,TypeScript 编译器就会知道该属性会被明确地赋值。
类型拓宽(Type Widening) 类型缩小(Type Narrowing) 03 编译选项 tsconfig.json💕 编译选项 include 示例: [path1,path2…]
作用:规定需要编译的路径
exclude 示例: [path1,path2…]
作用:排除规定不需要编译的路径
常见的编译选项:
选项
类型
默认
描述
target
string
“ES3”
指定ECMAScript
目标版本
module
string
target === “ES6” ? “ES6” : “commonjs”
指定生成哪个模块系统代码
lib
string[]
编译过程中需要引入的库文件的列表。
outDir
string
重定向输出目录。
outFile
string
将输出文件合并为一个文件。
removeComments
boolean
false
编译后移除注释
allowJs
boolean
false
是否编译 js 文件
checkJs
boolean
false
在 .js
文件中报告错误。与 --allowJs
配合使用。
noEmitOnError
boolean
false
编译,但不生成编译后的文件
noEmitOnError
boolean
false
报错时不生成输出文件
alwaysStrict
boolean
false
以严格模式解析并为每个源文件生成 "use strict"
语句
noImplicitAny
boolean
false
在表达式和声明上有隐含的 any
类型时报错。
03 使用 webpack 打包 ts 代码 📦 webpack 配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 const path = require ('path' )const HtmlWebpackPlugin = require ('html-webpack-plugin' )const { CleanWebpackPlugin } = require ('clean-webpack-plugin' )module .exports = { mode : 'development' , entry : './src/index.ts' , output : { filename : 'bundle.js' , path : path.resolve (__dirname, 'dist' ), environment : { arrowFunction : false , }, }, module : { rules : [ { test : /\.ts$/ , use : [ { loader : 'babel-loader' , options : { presets : [ [ '@babel/preset-env' , { targets : { ie : 11 , }, corejs : '3' , useBuiltIns : 'usage' , }, ], ], }, }, 'ts-loader' , ], exclude : /node_modules/ , }, ], }, plugins : [ new CleanWebpackPlugin (), new HtmlWebpackPlugin ((options = { template : './src/index.html' })), ], devServer : { static : { directory : path.resolve (__dirname, 'public' ), }, port : 8081 , }, resolve : { extensions : ['.ts' , '.js' ], }, }