typescript 是 javascript 的一个超集,typescript 可以运行于任何系统,并且是开源免费的。
typescript 有以下几个特点:
npm install typescript -g
npm install typescript -D
tsc -V
tsc 01.ts
# 或开启监听
tsc 01.ts -w
01.ts
const hhh = 'ok'
执行 tsc 01.ts
01.js
var hhh = 'ok';
TS 支持对编译过程使用配置项自定义
执行以下命令创建配置项 tsconfig.json
tsc --init
然后执行以下命令使用配置项的定义进行监测
tsc -w
配置 | 说明 |
---|---|
noImplicitAny | 禁止使用隐含的 any 类型,如函数参数没有设置具体类型 |
strictNullChecks | 开启时不否允许将 null、undefined 赋值给其他类型比如字符串 |
target | 转换成 JS 的版本 |
strict | 是否严格模式执行 |
module |
当没有明确设置类型时,系统会根据值推断变量的类型
下例中系统会根据值推断 hj 变量为 string,当将 hj 设置为 18 的 number 类型时编译时将报误
let hj = 'ok'; // let hhh: string
hj = 18; // 不能将类型“number”分配给类型“string”
ts 中的数值类型包括了小数、负数、整数
let hj = 1 // let hj: number
hj = 1.1
hj = -111
值为 true 或 false 会被推断为 boolean 类型
let state = true; // let state: boolean
下面是数组类型的推断结果,表示数组内容值为字符串
const hj = ['okk', 'hj'] // const hj: string[]
下面会推断数组允许的值为字符串或数值
const hj = ['ok', 'hj', 1] // const hj: (string | number)[]
hj.push(2, 'hk') // 数组允许数值、字符串类型,所以编译通过
hj.push(true) // 类型“boolean”的参数不能赋给类型“string | number”的参数
const hj = {
name: 'hj',
age: 20,
status: true,
otherInfo: {
desc: '前端'
},
skill: ['Node.js', 'Webapck']
}
推断结果如下
const hj: {
name: string;
age: number;
status: boolean;
otherInfo: {
desc: string;
};
skill: string[];
}
字符串使用 string 来声明
const hj: string = 'hj'
在 TS 中不区分整数与浮点数,都使用 number 来声明
const hj:number = 100
使用 boolean 来声明布尔类型
const hj: boolean = true
下面是对数组值类型为字符串
let hj2: string[] = []
hj.push('hj', 'ok')
下面是对数组值类型为字符串或数字
let hj2: (string | number)[] = []
hj2.push('hj', 2)
下面是数据限制类型为字符串或 由数字组成的数组
let hj3: string | number[] = []
hj3 = 'hj'
hj3 = [1, 2]
也可以使用泛型来声明数组(泛型的详细使用后面内容会介绍)
let hj:Array<string> = []
hj.push('hj', 'ok')
元组与数组类似,但元组要为每个值进行类型声明。
数组只是定义了值的类型,并没有约束某个位置的值必须是什么类型,请看下例
const arr: (number | string | boolean)[] = ['hj', 2030, true];
arr[1] = 'hj' // 不会报错,可以将原来是数值的更改为字符串,这是数组允许的类型范围
arr[10] = 'hj' // 不会报错,类型也是允许的
console.log(arr);
下面看使用元组来限制值的类型
const hj: [string, number] = ['hj', 2030]
hj[0] = true // 报错,第一个值必须是字符串
下面是声明对象类型但不限制值类型
let hj: object
hj = {name: 'hj'}
hj = {} // 使用字面量声明对象
hj = [] // 数组是对象
hj = function () {} // 函数是对象
hj = Object.prototype // 原型对象
hj = 'hj' // 报错,不能将类型“number”分配给类型“object”
限定对象值类型
let hj: {name: string, year: number}
hj = {name: 'hj', year: 2010}
hj.a = 1 // error: 类型“{ name: string; year: number; }”上不存在属性“a”
属性后面跟上?
用来指定 url 为可选值,这样的属性是非必填项
let hj: {name: string, year: number, url?: string}
hj = {name: 'hj', year: 2010}
使用 any 指包含所有值的顶部类型,所以 any 不进行类型检查,等于关闭了 TS 对该变量的严格类型校验
可以将 any 视为所有类型的组合表示
let hj: string | boolean | number;
hj = 'hj'
let hj: any
hj = 'hj'
下面是设置基本 any 的示例
let hj: any;
// 以下赋值不会报错
hj = "hj";
hj = 2010;
hj = true;
hj = [];
hj = {};
hj = class {};
在数组中使用 any 类型,可以设置任意类型的值
let hj: any[] = []
hj.push(1, '2', true, undefined, null, function() {})
为对象属性设置类型
let hj:{
name: any,
year: any
}
// 以下设置都不会报错
hj = {name:'hj', year: 2010}
hj = {name: 2010, year: 'hj'}
any 太过宽泛所以不建议使用,他会丢失 TS 的严格类型校验,比如下面的示例并不会报错
let hj: any
hj.get() // 不会报错
下面再来看一下对象的使用 any 类型造成的问题
class Hj {
constructor() { }
get = () => 'Hj'
}
const obj: any = new Hj;
console.log(obj.get());
obj.show() // 虽然obj原型链中没有show方法,但编译过程不会报错
所以上例需要指定正确的 Hj 类型,而不是使用 any
...
const obj: Hj = new Hj;
...
能过设置 tsconfig.json 的 noImplicitAny = true
配置项,可以禁止隐含的 any 类型。以下代码会在编译时报错
function sum(a, b) { // 参数“a”隐式具有“any”类型, 参数“b”隐式具有“any”类型
return a + b
}
// 需改为如下
function sum(a: number, b: number) {
return a + b
}
unknown 类型也是顶部类型这与 any 一样
as
类型断言来明确类型下面是 any 与 unknown 赋值上的区别,unknown 需要明确类型后赋值,any 则不需要
let hk: any = 'hj'
let hj: unknown = 'hj'
let a: string = xj
let b: string = hj // 报错: 'unknown'未知类型不能赋值给'string'类型
// unknown 类型需要明确类型后赋值
let c: string = hj as string
可以把任何值赋值给 unknown 类型,但在使用时需要指明类型
let hj: unknown
hj = 'houdunren'
hj = 100
//在使用时,TS不知道是什么类型,所以需要使用类型断言进行告之
let c = hj as number + 20
使用 keyof 类型工具时 unknown 与 any 的区别
type HJ<T> = { [P in keyof T]: string }
// {[x: string]: string;}
type HK = HJ<any>
// 结果为{},因为 keyof unknow 是never,所以被忽略了
type K = HJ<unknown>
不同类型赋值时会报错
let hj: string = 'hj'
let a: number = hj as number // 类型 "string" 到类型 "number" 的转换可能是错误的,因为两种类型不能充分重叠。如果这是有意的,请先将表达式转换为 "unknown"
这里需要使用 unknown 做个中间层转换,将其先转换为 unknown 未知类型,再转换为 string 类型
let hj: string = 'hj'
let a: number = hj as unknown as number // 不会报错
any 与 unknown 在类型检查上是有区别的
let hj: any
hj.show(); // any不进行类型检查,所以不会报错
let hk: unknown
hk.show(); // unknown进行类型检查,unknown是未知类型所以报错
使用 any 类型 ts 不进行类型校验,所以在编译时不会报错,但执行编译后的 js 后会显示 NaN
function get(val: any) {
val = val * 100;
return val
}
console.log(get('hj')); // NaN
使用 unknown 类型时,结合 typeof 进行类型判断,根据不同类型使用不同逻辑
function get(val: unknown) {
if (typeof val === 'number') {
return val * 100;
}
return 0
}
console.log(get(2)); // 200
void 类型的值为 null 或 undefined,常用于对函数返回值类型定义
strict
)时,void 值只能是 undefined(有关 TS 配置会在后面章节介绍)void 类型的值可以是 null 或 undefined,但如果 TS 配置开启了 strict
或 strictNullChecks
则不允许 void 为 null
let hj: void
hj = undefined
hj = null
void 不允许设置其他类型
let hj: void
hj = 'hj' // 不能将类型“string”分配给类型“void”
TypeScript 中,不返回任何内容的 void 函数实际上返回的是 undefined
function hj(): void {}
console.log(hj()) // undefined
经过 void 限定后是不允许函数返回内容的,所以以下代码将报错
function hj(): void{
return 'hj' // 不能将类型“string”分配给类型“void”
}
never 是任何类型的子类型,可以赋值给任何类型,没有类型是 never 的子类型。
never 类型的特点
function hj(): never{
throw new Error("出错了")
}
never 是所有类型的子类型,可以分配给任何类型,所以下面类型为 string
type HJ = never extends string ? string : boolean // string
其他类型不可以分配给 never 类型
type HJ = string extends never ? string : boolean // boolean
never 是所有类型的子类型,所以下面实际类型是 string | number
type HJ = never | string | number // string | number
null 与 undefined 也是对变量类型,用于定义值为 null 或 undefined
let hj: null = null
let hk: undefined = undefined
console.log(hj, hk); // null undefined
下面是函数返回值的使用
function getName(): string | null{
return null
}
console.log(getName()); // null
当配置项启用 strictNullChecks
时,null 与 undefined 只能赋值给 void、null、undefined 类型
let hj: string = undefined; // 配置strictNullChecks = true 时将报错
union 联合类型是多个类型的组合,使用 |
进行连接,|
类似于 javascript 中的 ||
或运算符。
下面是为变量声明字符串或数值类型
let hj: string | number = 'hj'
hj = 2010
下面是为数组声明多种类型
let hj: (string | number | boolean)[] = []
hj.push('hj', 2010, true)
也可以使用泛型方式声明(泛型的详细使用后面内容会介绍)
let hj: Array<string | number | boolean> = []
hj.push('hj', 2010, true)
下面是 TS 自动推断的函数类型
let hj = () => 'hj' // let hj: () => string
hj = '1' // 不能将类型“string”分配给类型“() => string”
使用 Function
来声明函数类型,注意是大写,这与 string/number/boolean 是有区别的
let hk: Function = () => 'hk'
hj = 1 // 不能将类型“number”分配给类型“Function”
通过对函数参数类型的声明,让函数更加稳定
const hj: Function = (a: number, b: number) => a + b
hj(1, 2) // 3
下面的函数没有声明函数参数类型,所以其是不稳定的
const hj : Function = (a, b) => a + b
hj('1', 2) // 12
可选参数可以通过 ?
来声明
const hj: Function = (a: number, b: number, c?: number) => {
c = c || 0
return a + b + c
}
console.log(hj(1, 2)) // 3
如果想要设置默认值就不要设置可选参数?
了,因为它们是互相矛盾的
const hj: Function = (a: number, b: number, c: number = 0) => a + b + c
console.log(hj(1, 2)) // 3
下面函数的参数类型都是number,那么返回值类型也应当是number
const hk = (a: number, b: number): number => a + b
function hj (a: number, b: number): number {
return a + b
}
const h = function (a: number, b: number): number {
return a + b
}
下面函数的参数类型都是number,返回值类型设置为string显然编译会报错
function hj (a: number, b: number): string {
return a + b // 不能将类型“number”分配给类型“string”
}
如果函数没有返回值你可以不写返回值类型,但我建议你写上 void,毕竟这样在函数第一行就可以看到返回值类型,并且这个函数也更加稳定
const hj = (a: string) : void => console.log(a);
有时多个函数会用到相同的类型的参数,比如下面的示例
let addUser = (user: { name: string; age: number }): void => {
console.log('添加用户')
}
let updateUser = (user: { name: string; age: number }): void => {
console.log('更新用户')
}
updateUser({ name: '后盾人', age: 18 })
我们可以使用 type
对参数对象进行声明,通过这种复用的手段可以很好的优化代码
// 使用 `type` 对参数对象进行声明
type userType = { name: string; age: number }
let addUser = (user: userType): void => {
console.log('添加用户')
}
let updateUser = (user: userType): void => {
console.log('更新用户')
}
updateUser({ name: 'hj', age: 18 })
对没有返回值函数结构的定义
let hj: () => void // 定义函数结构
hj = (): void => console.log('hj') // 实现函数
下例是对 hj 函数的定义
let hj: (a: number, b: number) => number
hj = (x: number, y: number): number => {
return x + y
}
也可以在声明函数时就定义函数的结构
let hj: (a: number, b: number) => number = (x: number, y: number): number => {
return x + y;
}
console.log(hj(2, 3));
参数是对象结构的函数定义
u
不定义类型结构,TS 也是可以推断出来的let addUser: (user: { name: string, age: number }) => boolean;
addUser = (u: { name: string, age: number }): boolean => {
console.log('添加用户');
return true;
}
上例中使用了重复的参数描述 { name: string, age: number }
,下面我们将参数对象使用 type 进行描述,就可以很好的优化代码
type userType = { name: string, age: number }
let addUser: (user: userType) => boolean;
addUser = (u: userType): boolean => {
console.log('添加用户');
return true;
}
addUser({ name: 'hj', age: 12 })
上面是将参数使用 type
进行了描述,我们也可以将函数结构使用 type 进行描述
type userType = { name: string, age: number }
type addUserFunc = (user: userType) => boolean;
let addUser: addUserFunc = (u: userType): boolean => {
console.log('添加用户');
return true;
}
addUser({ name: 'hj', age: 12 })
下面的求合函数接收多个参数
function sum(...args: any[]): number {
return args.reduce((s, n) => s + n, 0);
}
console.log(sum(1, 2, 3, 4, 5));
下面通过第二个参数接收剩余参数,来实现数据追加的示例
function push(arr: any[], ...args: any[]): any[] {
arr.push(...args)
return arr;
}
const hj: any[] = ['hj']
console.log(push(hj, 'hk', 'hh')); // [ 'hj', 'hk', 'hh' ]
静待 第二章:枚举与断言