【TypeScript】(一)快速上手TypeScript类型语法

1.概述

TypeScript(TS)是JavaScript(JS)的超集,它只是在JS的基础上加入的TS的类型系统,也就是说,我们已经编写好的历史JS脚本在切换到TS时,可以不经过任何修改也能正常运行。
同时,TS的类型系统会将我们编写的脚本中不符合语法的部分标记出来,让实际运行时会出现的异常、错误或者一些意想不到的情况提前暴露出来,减少运行时bug出现的概率。
本文中的部分内容来自于《TypeScript官方文档》,由于本文只是简单的快速上手,想要查看更详细的问题,可以去官网文档进行查看。
此外,TypeScript官网还提供了在线编辑的页面,《TypeScript:Playground》可以在这个页面编辑并测试TS脚本。

2.TS中的类型

TypeScript的基本类型是由JavaScript中的类型:boolean, bigint, null, number, string, symbol, undefined加上新增的any,never,null组成。

2.1.类型推断

对于一个不确定类型的变量,我们可以通过typeof获取到变量的类型,例如:

const num = 1;
const str = 'Hello TS!';
console.log(typeof num);
console.log(typeof str);

会打印出,“number"和"string”,也就是说即使没有显式的定义变量的类型,,TS也会通过类型推断由变量值来推导变量的类型。

2.2.类型定义

在某些函数中,我们需要获取到变量的类型才能做下一步操作,就可以使用typeof来进行判断,以一个简单的函数为例:

const plus = (x , y) => {
    if (typeof x === 'number' && typeof y === 'number') {
        return x + y;
    }
    throw new Error('请输入数字');
}
console.log(plus(1 , 2);
// 如果没有使用typeof,则会打印'12',这与函数的初衷不符
console.log(plus(1 , '2');

使用TS时,我们只需要显式的定义参数的类型,即可在编码阶段就获取到错误提示。
在这里插入图片描述
上图中的number就是TS的核心语法,定义变量的类型,接下来列举一些常见的定义变量类型的方式。

基础变量类型
定义变量的类型,就是在给变量加上一个限制,只有满足这个限制,才能够正常的赋值。

let num: number = 1;
let bool: boolean = false;
let str: string = 'Hello';
// any类型可以表示可以赋值为任意类型
let anything: any = 2;
anything = true;
anything = '任意类型';
......

接口定义变量类型
当我们需要使用到对象,并对对象中的字段进行限制时,就可以使用接口来限制变量的类型,例如:

interface User {
    name: string,
    age: number,
    nickname?: string
}

在下列的示例中,通过User接口来定义对象,并且只初始化了name字段,就会提示我们缺少了age字段的初始化操作:
在这里插入图片描述
但是没有提示我们缺少nickname,这是因为 ?:表示的是可选属性,可以不填。
注:interface中除了使用基础的变量类型限制外,接口中的字段也可以用其他接口来做限制,例如:

interface System {
    user: User,
    name: string
}

函数形参变量类型
与接口同理,在函数的形参中,我们也可以使用?:来表示可选参数。

const printParam = (p1: string , p2?: string) => console.log(`${p1},${p2}`);
printParam('Hello','world');
printParam('Hello');

两种调用方式,都不会提示语法错误。

2.3.复杂类型

对于简单类型来说,限制字段的值只能是同一种类型,而复杂类型就是限制字段可以是多种类型中的其中一个。
我们可以通过两种形式来对复杂类型进行定义:联合,泛型。

2.3.1.联合

使用联合类型的方式很简单,在多个类型之间使用|进行连接,使用或的语义即可,比如我们希望一个字段既可以是数字,又可以是字符串:

let multi: number | string = 1;
multi = 'abc';

用一个更加详细的例子来说明联合的作用,假如现在有一个获取对象中的字段值的函数,需要限制传入的key是对象中的字段,我们除了可以限制变量的基础数据类型以外,还可以限制变量只能被赋值为一些具体的值,例如下面例子中的形参key

interface User {
    name: string,
    age: number,
    nickname?: string
}
let user:User = {
    name :'张三',
    age: 18,
    nickname: '翼德'
}
// 此处 key 的值只能是 name,age,nickname中的其中一个,如果不是,则会提示错误
const getProperty = (obj:User , key:'name'|'age'|'nickname'): any => {
     return obj[key];
}

在这里插入图片描述
现在我们对User进行修改,加入一个性别字段gender,可以想象到的是,我们也需要同时在key后面加入gender字段才行,这种情况,我们可以用keyof来更灵活的处理,下面我们把getProperty()函数做一点修改。

interface User {
    name: string,
    age: number,
    nickname?: string,
    gender: number
}
let user:User = {
    name :'张三',
    age: 18,
    nickname: '翼德',
    gender: 0
}
// keyof User 等价于 'name'|'age'|'nickname'|'gender'
const getProperty = (obj:User , key:keyof User): any => {
    return obj[key];
}
let myName1:string = getProperty(user,'gender');

使用keyof之后,无论对象中的字段如何增减,我们都不需要再修改函数形参的类型了。

2.3.2.泛型

TS中的泛型大多数情况是使用在函数上(也可以使用在类上),我们可以把泛型理解为一种特殊的参数,在调用函数的时候,可以显式或隐式的传入类型。
identity函数为例,我们定义一个简单的函数,传入什么参数就返回什么参数。

function identity(arg: any): any {
	return arg;
}
let ident:string = identity(123);

因为返回值的类型是any,所以对于程序来说,并不能明确的知道返回值的类型到底是什么,所以当我们把ident的类型定义为string时,编译器也不会提示我们异常。
想要明确返回值的类型与传入的参数值类型一致,可以使用泛型来处理,对上面的代码做一点的修改:

function identity<T> (arg: T): T {
	return arg;
}
let ident:string = identity<number>(123);

identity后使用<>来定义类型的形参,调用时同样使用<>传入实际的类型,与上面定义的any类型不同,此时编译器会提醒我们出现了类型转换异常。
在这里插入图片描述
除了显示的调用以外,更常用的方式是在调用时不传入类型参数,使用类型推断来隐式的传入参数类型:

// 等价于 let ident:number = identity<number>(123);
let ident:number = identify(123);

以上面2.3.1中的函数为例,我们可以使用泛型来更加灵活的处理参数与返回值之间的关系。

interface User {
    name: string,
    age: number,
    nickname?: string
}
let user:User = {
    name :'张三',
    age: 18,
    nickname: '翼德'
}
// K extends keyof T,表示将K泛型的范围限制在T对象的字段名范围内
const getProperty = <T,K extends keyof T>(obj:T , key:K): T[K] => {
    return obj[key];
}

T[K]代入到上面的User中,如果传入的key等于name,则返回值T[K]类型为string,同理,传入age,则返回值类型为number

2.4.type关键字

除了直接使用类型关键字来限制变量的类型之外,我们还可以使用type关键字来定义类型,例如:

type a = number;
type b = string;
type c = boolean;
type abc = number | string | boolean;
......

使用起来也很简单,将类型关键字直接替换为type定义的变量即可:

let num:a = 1;
let str:b = 'abc';
// 复杂类型的type
let multi:abc = 2;
multi = 'bcd';

// 限制传入的是有length属性的参数
type strType = string | number[];
const getLength = (obj: strType) => obj.length;
console.log(getLength('daadsfadf'))
console.log(getLength([1,2,3,4,5]))
...... 

除此之外,type定义的变量,还可以被赋值为某些具体的值,表示在后续给变量赋值的时候,只能在这些值之间进行选择,例如:

type numType10 = 1 | 3 | 5 | 7 | 9; 
let num:numType10 = 1;
// 此处会有错误提示
let num:numType10 = 2; 

// 更多的例子
type checkedStateType = 'checked' | 'unchecked';
type lockStateType = 'locked' | 'unlocked';
type visibleStateType = true | false;

3. 总结

  1. TS的作用是在编译期将可能出现的问题尽可能的暴露出来,从而减少运行期出现bug的概率。
  2. 给变量定义的类型可以是基础数据类型,也可以是自定义的interface,还可以是某些具体的值。
  3. 可以通过|联合的方式将单一的类型限制组合为复杂的多个类型限制。
  4. 泛型的出现使用函数的封装更加灵活且复用性更高,此外也可以明确的对应出入参的关系。
  5. 使用typeof可以获取到变量的类型,使用keyof可以获取到对象的字段名类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

挥之以墨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值