一、联合类型
在TypeScript中,联合类型(Union Types)是指用“|”符号将多个类型组合成一个的类型。这种类型可以包含不同的类型,例如字符串、数字或对象。这些不同类型的值可以赋值给联合类型的变量。而在使用这个变量时,需要对不同类型的值进行类型检查,以确定当前变量的类型。
以下是一个简单的联合类型的示例:
let myVar: string | number;
myVar = "hello";
console.log(myVar.length);
myVar = 123;
console.log(myVar.length);
在这个例子中,myVar 变量的类型是 string | number,可以赋值为一个字符串或者一个数字。
- 当赋值为字符串时,可以调用 length 属性;
- 当赋值为数字时,就无法调用 length 属性,会导致编译报错。
使用联合类型可以增强代码的灵活性,提高代码的复用性和可维护性。在实际开发中,它也经常被用于函数参数的类型声明、接口中的属性类型声明等方面。
二、类型别名
TypeScript中的类型别名是一种非常有用的功能,它可以让我们给一种类型取一个自定义的名称。这个名称可以用来代替这种类型的任何地方,让代码更加简洁易读。
类型别名使用type
关键字来创建,具体的语法如下:
type MyType = string;
这个语法在TypeScript中是很常见的,它把MyType
这个名称与string
类型绑定在一起。现在,我们可以用MyType
来代替string
类型了:
let myStr: MyType = "Hello, world";
这里的let
语句声明了一个变量myStr
,它的类型是MyType
,也就是string
。我们可以看到,MyType
已经被成功地解析成了string
类型。
类型别名也可以用来定义更复杂的类型。例如,我们可以把一个对象类型定义为类型别名:
type Person = {
name: string;
age: number;
};
这里的Person
类型别名代表了一个对象类型,它包含两个属性:name
和age
,分别是string
类型和number
类型。现在,我们可以用Person
来定义一个对象:
let john: Person = {
name: "John",
age: 30
};
这里的john
变量的类型就是Person
,它是一个对象,带有name
和age
两个属性。
总之,类型别名是TypeScript中非常实用的功能,在定义复杂类型、简化代码等方面都有很大的帮助。
三、接口
TypeScript中的接口是用来定义代码之间交互的契约。它们可以用于描述对象、函数、类等的结构和规范。
下面是一些关于TypeScript接口的详细介绍:
- 对象接口
对象接口是在TypeScript中最常用的接口之一,它用于描述对象的结构。它可以定义对象应该有哪些属性,它们的类型是什么,以及它们是否是可选的。例如:
interface Person {
name: string;
age: number;
email?: string;
}
const person: Person = {
name: 'John',
age: 25
}
在这个例子中,Person接口定义了一个人的结构,包括一个必填的“name”属性,一个必填的“age”属性和一个可选的“email”属性。然后创建了一个名为“person”的对象,符合这个接口的定义。
- 函数接口
函数接口描述函数的形式参数和返回类型。例如:
interface Add {
(x: number, y: number): number;
}
const add: Add = (x, y) => {
return x + y;
};
在这个例子中,Add接口描述一个返回类型为number的函数,该函数接受两个参数:x和y,这两个参数均为number类型。
- 可索引类型接口
可索引类型接口用于描述可以像数组一样使用的对象。它允许您定义一个对象,该对象通过整数或字符串属性来访问其元素。例如:
interface StringArray {
[index: number]: string;
}
const myArray: StringArray = ['Hello', 'World'];
在这个例子中,StringArray接口允许我们通过数字来访问数组中的元素,因此myArray[0]将返回字符串“Hello”。
- 类接口
类接口描述一个类的实例结构。它可以描述一个类中必须实现的属性和方法。例如:
interface Animal {
name: string;
eat(food: string): void;
sleep(hours: number): void;
}
class Cat implements Animal {
name = 'Cat';
eat(food: string) {
console.log(`I'm eating ${food}`);
}
sleep(hours: number) {
console.log(`I'm sleeping for ${hours} hours`);
}
}
在这个例子中,Animal接口描述了一个动物实例应该具有的属性和方法。Cat类实现了这个接口,因此它必须定义name,eat和sleep方法以满足该接口。
总而言之,TypeScript接口提供了一种描述结构和规范的方式,从而使代码更加清晰和易于理解。通过使用接口,开发人员可以定义约束,使得代码更加稳健、可靠和易于扩展。
四、类型别名和接口的区别
接口和类型别名的区别主要在以下几个方面:
- 可以定义对象的形状的只能是接口,类型别名不能。
interface Person {
name: string;
age: number;
}
type MyPerson = {
name: string;
age: number;
}
- 类型别名可以定义任意类型的别名,而不仅仅是对象类型。例如:
type MyBoolean = boolean;
type MyFunction = (...args: any[]) => any;
- 类型别名可以定义联合类型、交叉类型、元组等复杂类型:
type MyType = string | number;
type MyObj = { a: string } & { b: number };
type MyTuple = [string, number];
- 接口可以定义扩展操作符(…)和可选属性:
interface Foo {
bar: string;
baz?: number;
[k: string]: any;
}
- 接口可以定义函数:
interface MyFunc {
(x: number, y: number): number;
}
- 接口可以被多次扩展,类型别名不能被扩展
接口可以被多次扩展,即一个接口可以在定义后被多次扩展,每次扩展都可以添加新的属性或方法。比如下面的示例:
interface Person {
name: string;
}
interface Person {
age: number;
}
const person: Person = {
name: "Tom",
age: 18,
};
在上面的示例中,定义了一个名为Person
的接口,并且在后面又定义了一个同名的接口。这是完全允许的,因为这两个接口在类型声明时是合并的。最终得到的Person
接口将包含name
和age
两个属性。
相比之下,类型别名不能被扩展。例如:
type Person = {
name: string;
};
type Person = {
age: number;
};
在上面的示例中,我们尝试定义了两个同名的类型别名Person
,但是这会引起编译错误。因为类型别名只能被定义一次。如果我们需要添加新的属性,就需要重新定义一个新的类型别名,而不是扩展已有的类型别名。
总的来说,接口更适合于描述对象的形状和方法,而类型别名更适合于为复杂类型定义别名,或定义函数类型、联合类型等。
五、类型断言
TypeScript的类型断言可以让开发者手动指定一个变量的类型,即告诉编译器当前变量的类型是什么,这个指定的类型可以与变量实际的类型不同。
类型断言可以在变量名前加上<type>
或者使用as
关键字,以下是两种写法的示例:
-
<type>variable
let someValue: any = "hello world";
let strLength: number = (<string>someValue).length;
-
variable as type
let someValue: any = "hello world";
let strLength: number = (someValue as string).length;
类型断言有以下几个特点:
-
它不会在运行时进行类型检查或类型转换,仅仅在编译时发挥作用。
-
如果类型断言的类型与变量实际的类型不同,则会导致编译错误。
-
类型断言可以应用于任何类型,包括原始类型、对象类型、函数类型等。