TypeScript Syntax

2024-03-11

[[JavaScript Syntax]] 语法相同:

  • 运算符
  • 条件语句
  • 循环语句

数组

数组的编号和 Python 一样是从 0 开始,也同样使用 [] 表示。定义时,TS 数组即在 TS 原始数据类型后面加 []

数组里的数据类型写法 1类型写法 2
字符串string[]Array<string>
数值number[]Array<number>
布尔值boolean[]Array<boolean>
大整数bigint[]Array<bigint>
符号symbol[]Array<symbol>
不存在null[]Array<null>
未定义undefined[]Array<undefined>
let 变量名称: 类型[]; // 默认方式,在类型后面加上方括号[]
let 变量名称: Array<类型>; // 泛型方式
// 赋值
let myArray: number[] = [1, 2, 42];
// 读取第一个值(从 0 开始)
console.log(myArray[0]);
// 查看长度
console.log(myArray.length);

遍历

let myArray: number[] = [1, 2, 42];
//遍历数组,循环体会执行4次,分别输出"a", "b", "c", "d"
for (let i = 0; i < myArray.length; i++) {
console.log(myArray[i]);
}

也可以使用 for…in 循环语句遍历:

for (let x in myArray) {
console.log(myArray[x]);
}

方法

转换

  • .toString():数组转字符串(逗号分隔)
  • .join():数组转换为自定义分隔字符
let myArray: number[] = [1, 2, 42];
console.log(myArray.toString()); // 1,2,42
console.log(myArray.join("-")); // 1-2-42

添加

  • .push():末尾添加新元素(可一次性添加多个)
  • .unshift():开头添加新元素(可一次性添加多个)
console.log(myArray.push(24)); // [ 1, 2, 42, 24 ]
console.log(myArray.unshift(100)); // [ 100, 1, 2, 42, 24 ]

移除

  • .pop():移除末尾元素
  • .shift():移除首个元素
console.log(myArray.pop()); // [ 100, 1, 2, 42 ]
console.log(myArray.shift()); // [ 1, 2, 42 ]
  • 移除

查找

  • .indexOf():从前往后查找,若无返回 -1
  • .lastIndexOf():从后往前查找,若无返回 -1
  • findIndex():自定义查找
console.log(myArray.indexOf(42)); // 2
console.log(myArray.lastIndexOf(24)); // -1
function findArray(value: number) {
return value >= 2;
}
console.log(myArray.findIndex(findArray)); // 1

排序

  • .sort():按指定规则排序
  • .reverse():反转顺序
const myArray: number[] = [11, 22, 1, 2, 42];
console.log(myArray.reverse()); // [ 42, 2, 1, 22, 11 ]
console.log(myArray.sort()); // [ 1, 11, 2, 22, 42 ]

对于 number[],使用 .sort() 排序时会先转换为字符串,然后再进行排序,因此 “11”,会在 “2” 前面,如果要避免此类问题,需要编写自定义排序函数:

function myFunction(value: number, nextValue: number) {
return value - nextValue;
}
myArray.sort(myFunction);
console.log(myArray); // [ 1, 2, 11, 22, 42 ]

合并与裁剪

  • .splice():从指定位置开始,删除指定个数,并插入指定值
  • .concat():合并数组(支持多个)
  • .slice():从第 n 个元素之后开始裁剪,并返回(包含第 n 个元素)
  • .reduce():根据函数遍历
  • .reduceRight():.reduce() 的反向
const myString: string[] = ["a", "b", "x", "y"];
// 从第三位开始移除 3 个元素,并插入两个元素
myString.splice(2, 2, "c", "d"); // [ 'a', 'b', 'c', 'd' ]
console.log(myString.concat(["z"])); // [ 'a', 'b', 'x', 'y', 'z' ]
// 从第二个元素(包含)开始裁剪
console.log(myString.slice(2)); // [ 'x', 'y' ]

元组

元组是 [[TypeScript]] 独有的新类型,通常用于表示长度较固定的数组,并可分别指定每个元素的类型,编译后在 [[JavaScript]] 中代码类型依然是数组。

const 变量名称: [类型1,类型2,...,类型n] = [值1,值2,...,值n];
const myTuple: [string, number, boolean] = ["a", 1, true];
// 可选元素
let myTuple1: [number, number, string?, number?] = [100, 200];

元组在修改时,长度和类型必须和原来的保持一致。

函数

TS 和 JS 的区别是在入参处和返回值增加了类型。

  • 无返回值函数:使用 void 定义(和 nullundefined 不可混用)

声明

命名函数

function mySum(x: number, y: number): number {
return x + y;
}

匿名函数

const mySum = function (x: number, y: number): number {
return x + y;
};

箭头函数

箭头函数在许多场景下可以取代传统的[[#匿名函数]],使代码更加简洁和易于维护。

const mySum = (x: number, y: number): number => {
return x + y;
};
// 只有单条执行语句时可省略 return
const mySum = (x: number, y: number): number => x + y;

方法函数定义

const obj = {
mySum(x: number, y: number): number {
return x + y;
},
};

自调用函数

在声明[[#匿名函数]]时将声明语句包裹在圆括号内,并同时在表达式最后补上圆括号即可。

(function (x: number, y: number) {
console.log(x + y);
})(4, 2);

参数

可选参数

可选参数在参数名后面加 ? 实现:

function mySum(x: number, y?: number): number {
if (y) {
return x + y;
}
return x + x;
}
console.log(mySum(4)); // 16
console.log(mySum(4, 2)); // 8

另外,可选参数必须在参数的尾部。

默认参数

默认参数在参数定义后面加 = 和默认值设置:

function mySum(x: number, y: number = 10): number {
return x + y;
}
console.log(mySum(4));
console.log(mySum(4, 2));

剩余参数

剩余参数可传递多个不确定的参数:

function myPrint(x: number, ...numbers: number[]): void {
for (let num of numbers) {
console.log(num);
}
}
myPrint(4, 2, 1, 2, 3);

重载

同时拥有多个调用签名的函数即为重载函数,这些签名拥有不同的参数个数和类型,可以执行不同的代码逻辑:

function sayHi(name: string): string;
function sayHi(name: string[]): string[];
function sayHi(name: string | string[]) {
// 这里没有返回数据类型
if (Array.isArray(name)) {
// 这里用的便是箭头函数
return name.map((n) => `Hi ${n}!`);
}
return `Hi ${name}!`;
}
const saying = sayHi("John");
console.log(saying); // Hi John!
const sayings = sayHi(["John", "Tom", "Petter"]);
console.log(sayings); // [ 'Hi John!', 'Hi Tom!', 'Hi Petter!' ]
  • 没有函数体的函数声明:它们都是重载签名,当调用该函数时,只能以符合这些签名之一的形式来调用函数。
  • 拥有函数体的函数声明:它并非重载签名,而是针对所有重载签名的具体实现,它的对应位置的参数类型和返回值类型必须能兼容前面所有重载签名中的类型。

[[Array]]

表达式形式声明

以表达式形式声明实现时,返回值必须显式声明:

let sayHi: {
(name: string): string;
(name: string[]): string;
};
sayHi = function (name: string | string[]): any {
if (Array.isArray(name)) {
// 这里用的便是箭头函数
return name.map((n) => `Hi ${n}!`);
}
return `Hi ${name}!`;
};
const saying = sayHi("John");
console.log(saying); // Hi John!
const sayings = sayHi(["John", "Tom", "Petter"]);
console.log(sayings); // [ 'Hi John!', 'Hi Tom!', 'Hi Petter!' ]

对象

TS 中的对象和 Python 的字典格式类似,一个重要区别是 TS 可以在对象中直接声明一个函数。

定义对象语法:

  • type:类型别名
  • interface:接口只能描述对象类型,不能用于描述非对象类型;
  • object:TS 新增类型,用于表示非原始类型(如 number, string, boolean, bigint 等)
  • Object:对象类型(不推荐)
  • {}:没有属性或方法的初始空对象(不推荐)
// type
type UserItem = {
//
};
// interface
interface UserItem {
//
}

表面上主要区别除了前缀不同之外,一个有 = 号,另一个没有。另外对象的命名一般使用[[大驼峰式命名法]]。

interface UserItem {
name: string;
age?: number; // ? 表示可选
enjoyFoods?: string[];
friendList?: UserItem[];
}
const petter: UserItem = {
name: "Petter",
friendList: [
{
name: "Tom",
},
],
};

修饰符

  • ?:可选
  • readonly:只读
  • [key: string]:索引签名
    • 字符串索引:键只能是字符串
    • 数值索引:键只能是数值
interface UserItem {
name: string;
age: number;
enjoyFoods?: string[]; // ? 表示可选
readonly friendList: UserItem[]; // 只读
}
// 索引签名
interface Person {
name: string;
age: number;
[str: number]: any; // 字符串索引
[num: number]: any; // 数值索引
}

合并

继承

在 TS 中接口的继承通过 extends 关键字实现:

interface Admin extends UserItem {
permissionLevel: number;
}

在继承时可通过 Omit 省略不需要的属性:

// 语法
type Omit<T, K extends string | number | symbol>

其中 T 代表已有的一个对象类型, K 代表要删除的属性名,如果只有一个属性就直接是一个字符串,如果有多个属性,用 | 来分隔开:

// 多个
interface Admin extends Omit<UserItem, "enjoyFoods" | "friendList"> {
permissionLevel: number;
}

接口继承中如果存在同名属性,但类型不同会导致编译错误。

交叉类型

通过 & 符号连接多个类型。

interface Colorful {
color: string;
}
interface Circle {
radius: number;
rollling: () => void;
}
type ColorfulCircle = Colorful & Circle;
let circle1: ColorfulCircle = {
color: "red",
radius: 5,
rollling: function () {
console.log("圆环滚动中!");
},
};

声明合并

当同时声明多个同名接口时,会自动合并为一个接口。

TS 中类的关键字和 Python 一样,都是通过 class 声明:

class UserBase {
name: string;
age: number;
// constructor 类似 Python __init__ 的初始化
constructor(userName: string, userAge: number) {
this.name = userName;
this.age = userAge;
}
// 方法
getName() {
console.log("class:", this.name);
}
}
// 实例化
const tom: UserBase = new UserBase("Tom", 42);
tom.getName();

继承

class User extends UserBase {
getAge() {
console.log("class extends: ", this.age);
}
}
const david: User = new User("David", 42);
david.getAge();

[[#对象]] 继承类:

// 如果类中包含方法不想实现,同样可以通过 Omit 去除
interface UserInterface extends Omit<UserBase, "getName"> {
// interface UserInterface extends UserBase {
enjoyFoods: string[];
}
const john: UserInterface = {
name: "John",
age: 42,
enjoyFoods: ["read", "game"],
};
console.log(john);

如果类中包含方法,接口在继承的时候,需要响应的实现,或者通过 Omit 去除。