TypeScript 可以看作是 JavaScript 的超集,不仅包含了 JavaScript 的所有内容,还拓展了语法、规定了类型约束,使得我们可以编写更干净、完整的代码。

类型注解

TypeScript提供了很多数据类型,通过类型对变量进行限制,称之为类型注解,使用类型注解后,就不能够随意变更变量的类型。

以下代码定义了一个字符串类型的变量,如果把它更改为数字类型时,代码编译阶段就会直接报错,提示 "Type 'number' is not assignable to type 'string'"。

这样保证变量的数据类型是固定的,那么它所能使用的方法也是确定的,不会出现变量本来是字符串,调用了 toUpperCase方法,后来在未测试到的某场景无意中把它改为数字类型后,调用 toUpperCase 直接报错的情况。

类型推导

当变量没有写数据类型时,ts内部会进行"类型推导",通过上下文赋值语句判断出该变量的数据类型,所以在类型注解较为简单时且其能准确自动推导出类型时,不写类型注解对代码质量也没有影响。

TS和JS共有的数据类型

TypeScript包含了JavaScript所拥有的数据类型,string、number、symbol、array、boolean、undefined、null、object。

其中,array 在定义的时候需要加上数组中的每一个数据的类型,是字符串、数字还是对象,这就使得数组中不可以写多种数据类型,如果有需要写多种数据类型的情况,可以直接不加类型注解,会自动进行【类型推导】。

let str: string = "string";
let num: number = 0;
let sym: symbol = Symbol("sym"); let arr1: string[] = ['alice', 'kiki', 'heidi']
let arr2: object[] = [{ name: 'alice'}]
let arr3: number[] = [1, 2, 3] let flag: boolean = false;
let und: undefined = undefined;
let nu: null = null;
let user = { name: "alice" };

上面的对象user也是没有加类型注解的,因为如果定义为 object 类型,无法获取和修改user对象上的属性。

这是因为user是object类型,但object类型上没有name属性,所以编译阶段会直接报错,所以对于对象可以不加类型注解,让其自动进行类型推导。

TS独有的数据类型

此外TypeScirpt还增加了很多JavaScript没有的数据类型

any

any是一个 "万金油" 的数据类型,表示 "任意" 数据类型,当变量的类型不确定且有可能发生变化时,可以定义为 any,此时变量的数据类型可以被随意更改,这其实会带来一些安全隐患。

比如下面的代码,将字符串当作函数使用,undefined使用数字的方法。因为是any类型,类型有可能被改成字符串、数字、布尔值,所以这样的代码不会被ts检测出问题,但在运行时肯定是会报错的。

let message: any = "message";
message(); message = 0;
message = undefined;
message.toFixed();

unknown

当一个变量的类型暂时不确定时,可以使用unknown用来限制其类型。

let flag = true
let result: unknown;
if (flag) {
result = 'Hello World'
} else {
result = 888
}

它和any有些像,但区别在于unknown是不能被赋值给除了any和unknown类型的变量,这样使得unknown的值不能被乱用到其它地方。

void

当函数没有返回值时,实际上返回的就是 undefined,void 通常用来表示函数返回值为 undefined 或者 null。

以下形式都是可以的

function add(): void {}

function sub(): void {
return undefined
} function mul(): void {
return null
}

当存在返回值时,编译是会报错的。

never

never 表示永远不会存在的类型,如果函数为死循环或者抛出异常时可以使用。

function foo(): never {
while (true) {}
} function catchError(): never {
throw new Error("error");
}

当我们封装工具函数时,开始规定的函数入参为 string 类型,但可能后续在其它人开发过程中,增加了 number 类型,为了避免维护时忘记对该类型数据进行处理,可以把入参赋值给 never 类型的变量,使得编译不通过来防止开发者逻辑疏漏。

tuple

tuple 意思是元组,和数组比较相似,但元组和数组还是存在以下区别。

  • 数组中元素数据类型建议是一致的,当数据类型不一致时考虑放到元组或者对象。
  • 元组中每个元素都有自己的特性,可以通过索引值获取。
// 数组
const array: string[] = ["alice", "kiki"];
// 元组
const tuple: [string, number] = ["alice", 20];

函数参数和返回值

声明函数时,在函数的每个参数后添加类型注解,以声明函数接受的参数类型,限制参数类型、参数个数。

在函数列表后面定义的类型注解,用于限制函数返回值类型的。

// 限制参入类型
function foo(message: string, no: number) {}
// 限制返回值
function baz(message: string): string {
return "string";
}

当入参类型/个数没有达到注解要求时,编译会标红以提醒。

对象类型、可选类型、联合类型 也都是可以用于规定函数入参的。

  • 对象类型,定义对象中每个参数的数据类型
  • 可选类型,用 ?表示,意味着该参数是非必传的,必须写在必选类型后面
  • 联合类型,用 | 表示,A | B 意味着入参类型是A和B中的任意一个
function getPoint(point: { x: number; y: number; z?: number }) {}
getPoint({ x: 10, y: 20 });
getPoint({ x: 10, y: 20, z: 30 }); function printId(id: string | number) {}
printId(1);
printId("alice"); function foo(message?: string) {}
foo("message");
foo();

当需要规定的类型比较长时,可以通过 type 关键字来定义类型别名

同时参数类型为【可选类型】可以理解该参数类型与 undefined 的【联合类型】,以下两个参数的入参本质上是一样的

function foo(message?: string) {}
foo("message");
foo(); function baz(message: string | undefined) {}
baz("message");
baz(undefined);

类型断言

有时候TypeScirpt获取到的类型是比较广泛的,这个时候可以使用类型断言,通过关键字 as 定义具体数据类型。

比如以下定义了两个类,Student 继承自 Person 类,且拥有自己的 studying 方法,定义sayHello方法,要求入参类型为Person,此时创建Student的实例对象student,并调用sayHello方法,传入student。

class Person {}
class Student extends Person {
studying(){}
} function sayHello(p: Person){}
const student = new Student()
sayHello(student)

以上代码在编译的时候是没有问题的,但如果在sayHello方法中想要调用Student实例对象的studying,是不可以的,因为虽然传入的参数是Student的实例对象,但它在TypeScript中只能被检测为Person类型,而Person上是没有studying方法的。

要想正确调用,需要使用类型断言规定入参的实际类型

function sayHello(p: Person){
(p as Student).studying()
}

非空类型断言

通过 !符号来使不能通过编译的代码不被标红提醒,但如果运行阶段代码存在问题,仍然是会抛出错误的。

字面量

通过const定义的变量的数据类型为字面量类型,字面量里的值就是该变量赋值的内容。

通过字面量类型,可用于规定变量的选择范围,比如 flex 布局的 direction 可以选择 row 或者 colums

type DirectionType = "row" | "colums";
let direction: DirectionType = "row";
direction = "colums";

当变量赋值为它类型注解中所没有包含的内容时,是会标红提醒的。

类型缩小

类型缩小表示当变量数据类型的范围比较大时,我们可以通过 if 、switch 、in 、typeof、instanceof 等方式进行判断来缩小的变量的类型,以达到更精准的操作。

比如一个函数的参数类型可能为 string 和 number,直接获取 length 属性 是会报错的。因为只有 string 类型的变量可以获取到 length,但在 number 类型上是不存在的,所以此时可以通过 typeof 来判断入参类型,这个判断步骤也称为"类型保护"。

这些ts中的类型注解,可以帮助开发者更好规范开发时的代码,减少线上故障~

以上就是关于TypeScript类型注解的内容,关于js和ts,还有很多需要开发者掌握的地方,可以看看我写的其他博文,持续更新中~

使用TypeScript类型注解,编写更干净的JS代码的更多相关文章

  1. 用Flow编写更好的js代码

    关于本文: 原文地址 翻译地址 译者:野草 本文发表于前端早读课[第897期] 你是否经常在debug那些简单可避免的bug?可能你给函数传参的时候搞错了参数的顺序,或者本来应该传个Number类型的 ...

  2. 使用 Promises 编写更优雅的 JavaScript 代码

    你可能已经无意中听说过 Promises,很多人都在讨论它,使用它,但你不知道为什么它们如此特别.难道你不能使用回调么?有什么了特别的?在本文中,我们一起来看看 Promises 是什么以及如何使用它 ...

  3. 编写更好的C#代码

    引言 开发人员总是喜欢就编码规范进行争论,但更重要的是如何能够在项目中自始至终地遵循编码规范,以保证项目代码的一致性.并且团队中的所有人都需要明确编码规范所起到的作用.在这篇文章中,我会介绍一些在我多 ...

  4. 如何编写高质量的js代码--底层原理

    转自: 如何编写高质量的 JS 函数(1) -- 敲山震虎篇   本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/7lCK9cHmunvYlbm ...

  5. 【转】编写更好的CSS代码

    原文转自:http://blog.jobbole.com/55067/ 编写好的CSS代码,有助提升页面的渲染速度.本质上,引擎需要解析的CSS规则越少,性能越好.MDN上将CSS选择符归类成四个主要 ...

  6. 编写更好的jQuery代码

    这是一篇关于jQuery的文章,写到这里给初学者一些建议. 现在已经有很多文章讨论jQuery和JavaScript的性能问题,然而,在这篇文章中我计划总结一些提升速度的技巧和一些我自己的建议来改善你 ...

  7. 编写更好的jQuery代码的建议

    讨论jQuery和javascript性能的文章并不罕见.然而,本文我计划总结一些速度方面的技巧和我本人的一些建议,来提升你的jQuery和javascript代码.好的代码会带来速度的提升.快速渲染 ...

  8. 编写更好的jQuery代码的建议(share)

    留个备份! 原文链接: Mathew Carella   翻译: 伯乐在线- yanhaijing译文链接: http://blog.jobbole.com/52770/ 讨论jQuery和javas ...

  9. 编写更好的jQuery代码(转)

    这是一篇关于jQuery的文章,写到这里给初学者一些建议. 原文地址:http://flippinawesome.org/2013/11/25/writing-better-jquery-code/ ...

  10. 可变类型的安全性——更锋利的C#代码小记(2)

    ReadOnlyCollection类型是.NET系统类库提供的一个只读集合类型,它与原来的List<string>不存在任何类型转换关系,因此可以从根本上阻止外部对其的修改操作using ...

随机推荐

  1. Golang每日一库之regex

    本文地址: https://www.cnblogs.com/zichliang/p/17387436.html Golang日库合集:https://www.cnblogs.com/zichliang ...

  2. ChatGPT 打字机效果原理

    一.背景 在初次使用 ChatGPT 时,我就被打字机的视觉效果吸引.总是感觉似曾相识,因为经常在一些科幻电影中看到,高级文明回传的信息在通讯设备的屏幕上以打字机效果逐步出现,在紧张的氛围下,输出人类 ...

  3. 聊聊Mybatis的实现原理

    使用示例 平时我们使用的一般是集成了Spring或是Spring Boot的Mybatis,封装了一层,看源码不直接:如下,看看原生的Mybatis使用示例 示例解析 通过代码可以清晰地看出,MyBa ...

  4. drf——反序列化校验源码(了解)、断言、drf之请求和响应、视图之两个视图基类

    1.模块与包 # 模块与包 模块:一个py文件 被别的py文件导入使用,这个py文件称之为模块,运行的这个py文件称之为脚本文件 包:一个文件夹下有__init__.py # 模块与包的导入问题 '' ...

  5. < Python全景系列-6 > 掌握Python面向对象编程的关键:深度探索类与对象

    欢迎来到我们的系列博客<Python全景系列>!在这个系列中,我们将带领你从Python的基础知识开始,一步步深入到高级话题,帮助你掌握这门强大而灵活的编程语法.无论你是编程新手,还是有一 ...

  6. GroundingDINO(一种开集目标检测算法)服务化,根据文本生成检测框

    背景 最近发现一个叫GroundingDINO的开集目标检测算法,所谓开集目标检测就是能检测的目标类别不局限于训练的类别,这个算法可以通过输入文本的prompt然后输出对应的目标框.可以用来做预标注或 ...

  7. hvv蓝初面试常见漏洞问题(上)

    1.SQL注入 漏洞成因: 可控变量 变量会带入数据库查询 变量不存在过滤或者变量过滤不严格 注入流程 判断是否有注入点 order by 判断字段数量 union select 报错查看注入点 使用 ...

  8. Golang扫盲式学习——GO并发 | (一)

    并发与并行 并发与并行的概念和区别 并行:同一个时间段内多个任务同时在不同的CPU核心上执行.强调同一时刻多个任务之间的"同时执行". 并发:同一个时间段内多个任务都在进展.强调多 ...

  9. ImageMagick 图像处理学习笔记

    Use ImageMagick to create, edit, compose, or convert bitmap images. It can read and write images in ...

  10. Win32 GUI 汇编

    获取句柄 API函数 GetModuleHandle 取模块句柄,lpModuleName 是一个指向模块名称字符串的指针,使用 NULL 获取当前程序句柄. invoke GetModuleHand ...