前言
TypeScript(TS)是什么?
TypeScript 是 JavaScript 的超集,这意味着 JS 中能实现的各种操作和语法,在 TS 中都可以实现。TS 添加了可选的静态类型检查 ,使得开发者可以在开发阶段就发现类型错误,从而提高代码质量和可维护性。此外,TypeScript 还包含了对类、接口、命名空间等面向对象编程特性的支持。
简单来讲,TypeScript(TS)就是为 JS 提供了一个类型限定,它的主要目的是让 js 代码更加健壮和可维护,在学习 TS 时,最重要的一环就是理解并熟练运用类型系统。今天咱们先来简单上手一下TS和了解它的基本类型
正文
TS安装
less
npm i -g typescript // 全局安装 TypeScript
如果想要运行 ts 则可以安装 ts-node。
less
npm i -g ts-node // 全局安装 ts-node
ts-node 是一个实用工具,允许你在 Node.js 中直接运行 TypeScript 文件。例如ts-node add.ts
,将执行 app.ts 文件中的代码,而无需显式地使用 tsc 命令将其编译为 JavaScript。
TS类型
基本类型
和 JS 保持一致,比如 string
、number
和 boolean
。在 TS 中,你可以显式地为变量指定类型:
ini
let name: string = "hello";
let age: number = 22;
let isSingle: boolean = true;
let a:symbol=Symbol();
let b:bigint=123n;
let u: undefined = undefined; // 表示变量尚未被赋值
let n: null = null; // 显示将值设为空
null 和 undefined
null
和 undefined
可以赋值给任何类型,此时可以理解为它们是所有类型的子类型。如果你在 tsconfig.json
中开启了 strictNullChecks
选项,null
和 undefined
只能赋值给 void
或它们各自的类型。
默认未开启 strictNullChecks
选项
ini
let name: string = "John";
name = null; // 没有报错
name = undefined; // 也没有报错
开启了 strictNullChecks
选项
ini
let name: string = "John";
name = null; // 报错:不能将 null 赋值给 string 类型
name = undefined; // 报错:不能将 undefined 赋值给 string 类型
let nullableName: string | null = "John";
nullableName = null; // 这样就可以,因为类型包含了 null
在启用了 strictNullChecks
的情况下:
- 只有明确标注为
null
或undefined
的类型(例如string | null
、number | undefined
)才能接收null
或undefined
作为值。 - 其他类型的变量(如纯粹的
string
或number
)不能被赋值为null
或undefined
。
void
类型
void
类型通常用于函数没有返回值的情况,它表示没有任何类型
c
function logMessage(message: string): void {
console.log(message);
}
any类型
1、代表任何类型,any 可以赋值成任意类型的值,可以赋给其他类型。
ini
let x: any;
x = 1;
x = "hello";
let y: boolean = true;
y = x;
2、在 ts 文件中可以创建一个 tsconfig.json 文件进行配置,其中noImplicitAny
是一个重要的编译选项,默认值为 false
,它控制着 TypeScript 编译器是否允许隐式的 any
类型。当你设置 "noImplicitAny": false
时,意味着你允许 TypeScript 编译器接受隐式的 any
类型。
json
"compilerOptions": {
"noImplicitAny": true // 不允许any
}
如果 noImplicitAny
设置为 true
,那么这段代码在编译时会产生一个警告或错误,指出 x
和 y
的类型没有显式指定。编译器会提示你需要为这些变量提供类型注解。
不推荐使用any!这会让TypeScript变为"AnyScript"(失去TS类型保护的优势)
因为当值的类型为any时,可以对该值进行任意操作,并且不会有代码提示。
尽可能的避免使用any类型,除非临时使用any来'避免'书写很长、很复杂的类型!
其他隐式具有any的情况:
1、声明变量不提供类型也不提供默认值
2、函数参数不加类型。
unknown类型
unknown
类型是一个更安全的 any
类型。与 any
不同,在将 unknown
类型赋值给其他类型之前,必须先进行类型检查或`类型断言
javascript
function processValue(value: unknown) {
if (typeof value === "string") {
console.log(`String value: ${value}`);
}
else if (typeof value === "number") {
console.log(`Number value: ${value}`);
}
else if (typeof value === "object" && value !== null) {
console.log("Object value:", value);
}
else {
console.log("Unknown type");
}
}
processValue("Hello, world!"); // 输出: String value: Hello, world!
processValue(42); // 输出: Number value: 42
processValue({ id: 1 }); // 输出: Object value: { id: 1 }
processValue(true); // 输出: Unknown type
使用之前你若不进行类型检查或类型断言就会报错
因此 unknown
提供了比 any
更好的类型安全保障。
never 类型
never
类型表示永远不会有值的类型。通常用于表示那些总是会抛出错误
或不会有返回值的函数
。因此被赋值会报错,哪怕是 any
typescript
function error(message: string): never {
throw new Error(message);
}
never
也可以表示不可能的类型,例如当类型联合被完全穷尽时:
typescript
type Animal = "cat" | "dog" | "dolphin";
function handleAnimal(animal: Animal) {
switch (animal) {
case "cat":
console.log("It's a cat.");
break;
case "dog":
console.log("It's a dog.");
break;
case "dolphin":
console.log("It's a dolphin.");
break;
default:
// 这里的 never 类型确保我们已经处理了所有可能的情况
const exhaustiveCheck: never = animal;
throw new Error(`Unhandled case: ${exhaustiveCheck}`);
}
}
as类型断言
javascript
const aLink: HTMLAnchorElement
const aLink=document.getElementById('link')as HTMLAnchorElement
1、使用as关键字实现类型断言。
2、关键字as后面的类型是一个更加具体的类型(HTMLAnchorElement是HTMLElement的子类型)
3、通过类型断言,aLink的类型变得更加具体,这样就可以访问a标签特有的属性或方法了。
另一种语法,使用<>语法,这种语法形式不常用知道即可
ini
const aLink=<HTMLAnchorElement>document.getElmentById('link')
tips:在浏览器的控制台,通过console.dir()打印DOM元素,在属性列表后面,即可看到该元素的类型。
再来个例子:
typescript
let value: unknown = "Hello, TypeScript";
// 使用类型断言将 unknown 类型的值转换为 string 类型
let strValue: string = value as string;
console.log(strValue.toUpperCase()); // 输出: HELLO, TYPESCRIPT
类型推论
在TS中,某些没有明确指出类型的地方,ts的类型推论机制会帮助提供类型换句话说:由于类型推论的存在,这些地方,类型注解可以省略不写!
发生类型推论的2种场景:
1、声明变量并初始化时
2、决定函数返回值时
typescript
let age:number
let age=18
function add(num1:number,num2:number):number
function add(num1:number,num2:number){
return num1+num2
}
推荐:能省略类型注解的地方就省略,如果不知道类型,可以通过鼠标放在变量名称上,利用vscode的提示来查看类型
类型别名
为类型创建一个新的名称,用关键字 type
1、使用type关键字来创建类型别名。
2、类型别名(比如,此处的CustomArray)可以是任意合法的变量名称
3、创建类型别名后,直接使用该类型别名作为变量的类型注解即可。
函数类型
单独指定参数、返回值的类型
函数声明方式:
typescript
function add(num1:number,num2:number):number{
return num1+num2
}
函数表达式方式:
typescript
const add=(num1:number,num2:number):number=>{
return num1+num2
}
同时指定参数、返回值的类型
typescript
const add:(num1:number,num2:number)=>number =(num1,num2)=>{
return num1+num2
}
空类型:void
c
function greet(name:string):void{
console.log('Hello',name)
}
可选参数
在可传可不传的参数名称后面添加?(问号)
sql
function mySlice(start?:number,end?:number):void{
console.log('起始索引',start,'结束索引:',end)
}
对象类型
typescript
let person:{name:string;age:number;sayHi():void;greet(name:string):void}={
name:'刘老师',
age:18,
sayHi(){},
greet(name){}
}
1、如果一行代码只指定一个属性类型(通过换行来分隔多个属性类型),可以去掉;(分号)
2、方法的类型也可以使用箭头函数形式(比如:{sayHi:()=>void})
可选属性
typescript
function myAxios(config:{url:string;method?:string}){
console.log(config)
}
可选属性的语法与函数可选参数的语法一致,都用问号来表示
接口
当一个对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的目的。
typescript
interface IPerson{
name:string
age:number
sayHi():void
}
let person:IPerson={
name:'jack',
age:19,
sayHi(){}
}
1、使用interface关键字来声明接口
2、接口名称可以是任意合法的变量名称。
3、声明接口后,直接使用接口名称作为变量的类型。
4、因为每一行只有一个属性类型,因此,属性类型后没有;(分号)
interface(接口)和type(类型别名)的对比:
相同点:都可以给对象指定类型。
不同点:
1、接口只能为对象指定类型
2、类型别名:不仅可以为对象指定类型,实际上可以为任意类型指定别名。
type NumStr =number|string
接口继承
extends
如果两个接口之间有相同的属性或者方法,可以将公共的属性或者方法抽离出来,通过继承来实现复用。
typescript
interface Point2D{x:number;y:number}
interface Point3D extends Point2D {z:number}
1、使用extends(继承)关键字实现了接口Point3D继承Point2D.
2、继承后,Point3D就有了Point2D的所有属性和方法(此时,Pointe3D同时有xyz三个属性)
元组
使用number[]的缺点:不严谨,因为该类型的数组中可以出现任意多个数字。
更好的方式:元组(Tuple)
元组类型是另一种类型的数组,它准确的知道包含多少个元素,以及特定索引对应的类型
typescript
let position: [number,number]=[39.5427,116.2317]
1、元组类型可以准确的标记出有多少个元素,以及每个元素的类型。
2、该示例中,元素有两个元素,每个元素的类型都是number.
同样,元组类型用于表示已知数量和类型的元素的数组。与数组类型不同,元组中的每个元素类型可以不同。
typescript
let tuple: [string, number, boolean] = ["hello", 42, true];
元组的长度是固定的,并且每个位置上的类型是确定的。这在需要表示固定结构的数据时非常有用,例如函数返回多个值的情况。当 push 的元素超出时类型必须是已有的,否则报错:
字面量类型
使用模式:字面量类型配合联合类型一起使用
使用场景:用来表示一组明确的可选值的列表。
比如,在贪吃蛇游戏中,游戏的方向的可选值只能是上下左右的任意一个
css
function changeDirection(direction:'up'|'down'|'left'|'right'){
console.log(direction)
}
解释:参数direction的值只能是up/down/left/right中的任意一个
优势:相比于string类型,使用字面量类型更加精确、严谨
枚举类型
枚举的功能类似于字面量类型+联合类型组合的功能,也可以表示一组明确的可选值。
枚举:定义一组命名常量。它描述一个值,该值可以是这些命名常量中的一个
css
enum Direction{Up,Down,left,Right}
function changeDirection(direction:Direction){
console.log(direction)
}
1、使用enum关键字定义枚举。
2、约定枚举名称,枚举中的值以大写字母开头
3、枚举中的多个值之间通过逗号分隔
4、定义好枚举后,直接使用枚举名称作为类型注解。
注意:形参direction的类型为枚举Direction,那么,实参的值就应该是枚举Direction成员的任意一个,类似于JS中的对象,直接通过点.语法访问枚举成员。
数字枚举
问题:我们把枚举成员作为了函数的实参,它的值是什么呢?
注意:枚举成员是有值的,默认为:从0开始自增的数值
我们把枚举成员的值为数字的枚举称为数字枚举,当然也可以给枚举中的成员初始化值
css
//Down-> 11、left->12、Right->13
enum Direction {Up=10,Down,Left,Right}
字符串枚举
枚举成员的值是字符串
ini
enum Direction{
Up='Up',
Down='Down'
}
注意:字符串枚举没有自增长行为,因此,字符串枚举的每个成员必须有初始值。
枚举的特点及原理
枚举是ts为数不多的非javaScript类型级扩展(不仅仅是类型)的特性之一。
因为:其他类型仅仅被当作类型,而枚举不仅用作类型,还提供值。
也就是说,其他的类型会在编译为js代码时自动移除,但是,枚举类型会被编译为js代码!
一般情况下,推荐使用字面量类型+联合类型组合的方式,因为相比枚举,这种方式更加直观、简洁、高效。
TS中的typeof运算符
众所周知,JS中提供了typeof操作符,用来在JS中获取数据的类型。
实际上,TS也提供了typeof操作符:可以在类型上下文中引用变量或属性的类型(类型查询)
使用场景:根据已有变量的值,获取该值的类型,来简化书写。
css
let p={x:1,y:2}
function formatPoint(point:{x:number;y:number}){}
formatPoint(p)
function formatPoint(point:typeof p){}
1、使用typeof操作符来获取变量P的类型,结果与第一种(对象字面量的类型)相同。
2、typeof出现在类型注解的位置(参数名称的冒号后面)所处的环境就在类型上下文(区别于Js代码)。
3、注意:typeof 只能用来查询变量或属性的类型,无法查询其他形式的类型(比如,函数调用的类型)
结尾
好啦,今天的分享就到这里结束了,今天介绍了一下TS简单的基本类型,相信小伙伴们应该都理解了叭,还有进阶的更复杂的TS的高阶类型体操我就下期在和大家分享哈!