Typescript|Basic

Intro

TypeScript 的目标是作为 JavaScript 程序的静态类型检查器————换句话说,是一种在代码运行之前运行的工具(静态),并确保程序的类型是正确的(已检查类型)

来看这么一个例子

1
2
3
4
// 访问 message 的 toLowerCase 方法并调用它
message.toLowerCase();
// 调用 message 函数
message();

假设 message 是这样定义的:

1
const message = "Hello World!";

那么第一行代码可以正常执行,但是第二行就会在运行时报错:TypeError: message is not a function

我们希望不要等到JS运行的时候计算出异常,当然对于JS中的原始数据类型,我们可以使用typeof来得到对应的数据类型,但是函数就不行,看下面的代码

1
2
3
function fn(x) {
return x.flip();
}

如果传入fn函数的入参不是并不支持flip(),那么在调用fn的时候内部也会出现异常,这种方式目前我们并没有一种合理的解决方式

因此实际上我们希望能在编写代码的时候就能分清楚代码会做什么事情,我们希望引入类型的概念,这里说的类型其实就是描述了什么值可以安全传递给 fn,什么值会引起报错

这个时候我们就可以引入Typescript,他是一个在JS实际运行之前的静态类型检查工具。开发者编写符合ts规范的代码,通过tsc编译得到最终不包含类型的js文件

环境准备

前置需要node,我们可以全局安装typescript

1
npm i -g typescript

但是更推荐在项目中独立安装(可能需要借助 npx 或者类似的工具才能便捷地运行 tsc 指令)

1
npm i --save-dev typescript

类型注解&TSC

1
2
3
4
5
6
7
function greeter(person) {
return "Hello, " + person;
}

let user = "Jane User";

document.body.textContent = greeter(user);

使用TS重写上述JS代码引入类型

1
2
3
4
5
6
7
function greeter(person: string) {
return "Hello, " + person;
}

let user = "Jane User";

document.body.textContent = greeter(user);

这个时候我们可以通过tsc编译上述ts代码,最终会生成一个js代码,可以发现编译生成的js和原先的js内容一致

上述greeter函数的入参,我们引入了类型,在TS官方中称其为Type annotations

我们可以修改 user的数据类型,将其改为数字/对象/数组,在IDE中或者是编译的时候都会给出错误信息,表示类型错误

对JS迁移的兼容

我们通过tsc编译写好的ts代码后,如果编译出现错误,实际上可能代码还是可以运行的,报错的时候仍然会产出js文件

这是因为,例如我们现在正把JS代码迁移到TS代码,并因为历史的原因,在ts的检查约束下产生了很多类型检查错误

这个时候难道就因为迁移到TS的原因,需要把所有的类型错误都修改吗?毕竟原先代码还是可以跑的、

因此TS的设计规则就是并不会对实际程序执行产生阻碍(默认配置下),只是起到运行前类型约束的效果

降级

TSC编译后生成的JS代码往往可能不是最新ES规范的JS代码,例如字符串模板${}这样的会被降级重写成字符串拼接的方式

1
2
3
`Hello ${person}, today is ${date.toDateString()}!`;

"Hello " + person + ", today is " + date.toDateString() + "!";

在默认情况下,TypeScript 会转化为 ES3 代码,这是一个非常旧的 ECMAScript 版本

我们可以通过编译器参数--target es2015来指定生成高版本的JS

Interfaces

1
2
3
4
5
6
7
8
9
10
11
12
interface Person {
firstName: string;
lastName: string;
}

function greeter(person: Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}

let user = { firstName: "Jane", lastName: "User" };

document.body.textContent = greeter(user);

我们使用一个接口来描述具有 firstName 和 lastName 字段的对象

在 TypeScript 中,如果两个类型的内部结构兼容,则它们是兼容的,因此上述代码中的 user 对象兼容 Person interface,可以作为 greeter 的入参,不需要显式 implements

和Class的区别

还有一个是class比较容易和这个混淆,简单来说interface主要是定义类型结构,用于类型约束。class更侧重oop,

前端领域,函数式和组合式 API 更常见(例如 React Hooks、Vue Composition API)

所以在实际开发中我们一般都是写interface居多

一般我们都是用interface定义业务对象的数据结构,这个结构可能来自 API 响应

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
<!-- UserCard.vue -->
<script lang="ts" setup>
import { defineProps } from 'vue'

// 用 interface 定义 props 类型
interface User {
id: number
name: string
avatarUrl?: string
}

// 明确标注 props 的结构
const props = defineProps<{
user: User
showAvatar?: boolean
}>()
</script>

<template>
<div class="user-card">
<img v-if="showAvatar && user.avatarUrl" :src="user.avatarUrl" alt="avatar" />
<h3>{{ user.name }}</h3>
</div>
</template>

请求用户信息,封装用户对象

1
2
3
4
5
6
7
8
9
interface ApiResponse<T> {
code: number
message: string
data: T
}

function fetchUser(): Promise<ApiResponse<User>> {
return axios.get('/api/user')
}

登录接口参数约束

1
2
3
4
5
6
7
8
interface LoginPayload {
username: string
password: string
}

function login(payload: LoginPayload) {
// ...
}

strict

不同的用户会因为不同的原因选择使用 TypeScript 的类型检查器

1)在 TypeScript 默认提供的开发体验下,类型是可选的,推断会使用最松散的类型,对于潜在的 null/undefined 类型的值也不会进行检查

2)Typescript 还提供了不同细粒度的检查限制约束

对于1)如果正在迁移现有的 JavaScript 项目,第一种配置方式是最佳实践

对于2)需要额外的配置项,我们可以在项目的 tsconfig.json 中设置 strict: true 一次性开启所有严格校验,也可以单独配置;单独配置下需要注意这两个配置项

  • noImplicitAny:不允许最宽泛的类型推断,类型都用any = 没用ts,启用 noImplicitAny 配置项,在遇到被隐式推断为 any 类型的变量时就会抛出一个错误。
  • strictNullChecks:防止null或者undefined错误

Typescript|Basic
http://example.com/2025/05/15/Typescript-Basic/
作者
Noctis64
发布于
2025年5月15日
许可协议