TS的函数
默认参数
在参数名后面加上问号y: number=100表示该参数为默认参数。如果该参数未赋值,默认为一百。
function foo(x: number, y: number=100) {
// ...
}
可选参数
在参数名后面加上问号?表示该参数为可选参数。可选参数必须位于必选参数后面。
可选参数和默认参数不能同时用在一个参数上
回调函数里永远不要写可选参数,除非你调用该函数时用不上该参数
function foo(x: number, y?: number) {
// ...
}
...args
在 TypeScript 中,...args 和 arguments 都是函数参数的特殊语法。
...args 是展开语法(Spread Syntax),它可以在函数的参数列表中使用,用于将一个数组或类数组对象展开成独立的参数。例如:
function foo(...args: number[]) {
console.log(args); // [1, 2, 3]
}
foo(1, 2, 3);
在上面的例子中,通过 ...args 将参数列表中的所有参数展开为一个数组。
arguments 是一个特殊的函数内部对象,它包含了所有传递给函数的参数。它是一个类数组对象,可以通过索引访问参数的值。例如:
function bar() {
console.log(arguments); // [1, 2, 3]
}
bar(1, 2, 3);
需要注意的是,arguments 是一个类数组对象,不是一个真正的数组,所以它没有数组的方法。在TS中它有专门的数据类型Iagreument,如果需要使用数组的方法,可以将 arguments 转换为一个真正的数组,例如使用 Array.from(arguments) 或者 Array.prototype.slice.call(arguments)。
在 TypeScript 中,推荐使用 ...args 语法来代替 arguments,因为 ...args 更加灵活且类型安全。
TS中的函数类型可以通过以下几种方式来定义:
函数表达式
function add(x: number, y: number): number {
return x + y;
}
箭头函数表达式
const multiply = (a: number, b: number): number => {
return a * b;
};
函数类型变量
type Operation = (a: number, b: number) => number;
const divide: Operation = (a, b) => {
return a / b;
};
使用函数关键字定义函数类型
type MathFunc = (x: number, y: number) => number;
let add: MathFunc = function(x: number, y: number): number {
return x + y;
};
这表示MyFuncType是一个函数类型,接受一个number类型的参数和一个string类型的参数,返回值为void。
可选参数和默认参数
function greet(name: string, age?: number, gender: string = 'unknown'): void {
console.log(`Hello, ${name}! You are ${age || 'unknown'} years old and your gender is ${gender}.`);
}
剩余参数
function sum(...numbers: number[]): number {
return numbers.reduce((acc, curr) => acc + curr, 0);
}
使用接口定义函数类型
interface MathFunc {
(x: number, y: number): number;
}
let add: MathFunc = function(x: number, y: number): number {
return x + y;
};
同样表示MyFuncType是一个函数类型,接受一个number类型的参数和一个string类型的参数,返回值为void。
函数属性
函数属性和类属性的差别
TS函数的属性和类的属性是两个不同的概念。
函数的属性是指在函数对象上定义的属性。在TS中,可以使用接口来定义一个函数的属性。例如:
interface Foo {
bar: string;
}
function myFunction(): void {
// ...
}
myFunction.bar = 'baz';
在上面的例子中,myFunction是一个函数对象,我们通过给myFunction对象添加一个bar属性来定义函数的属性。
类的属性是指在类中定义的属性。在TS中,可以使用类的成员变量来定义类的属性。例如:
class MyClass {
myProperty: string;
}
const myObject = new MyClass();
myObject.myProperty = 'myValue';
在上面的例子中,MyClass是一个类,myProperty是类的属性。我们通过实例化一个MyClass对象,并给myProperty属性赋值来使用类的属性。
加属性的函数类型
// 定义函数类型,是将函数作为对象定义的
type Func = {
(someArg: string): boolean
description: string
}
// 定义函数类型
function func1(arg: string): boolean {
console.log(arg)
return true
}
// 给函数类型添加属性
func1.description = '这是一个函数属性'
// 将定义好的对象类型赋值给函数,发现于此同时函数的属性也被添加进去了
function dosomething(fn: Func) {
console.log(fn.description)
}
dosomething(func1)
函数的this
在TypeScript中,this关键字用于表示当前函数的上下文对象。当一个函数作为方法被调用时,this指向调用该方法的对象。当一个函数作为普通函数调用时,this指向全局对象(在浏览器中是window对象)。
下面是一个使用this和接口的示例:
interface Person {
name: string;
age: number;
sayHello: () => void;
}
function greet(this: Person) {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
const person: Person = {
name: "John",
age: 30,
sayHello: greet
};
person.sayHello(); // 输出:Hello, my name is John and I am 30 years old.
在上面的示例中,我们定义了一个Person接口,它有name和age属性以及一个sayHello方法。然后,我们定义了一个greet函数,使用this参数将函数的上下文限制为Person类型。最后,我们创建了一个person对象,它符合Person接口的定义,将sayHello方法指向greet函数。当我们调用person对象的sayHello方法时,this指向person对象,所以在greet函数中可以访问到person对象的name和age属性。
对ts函数的个人理解,函数就类似一个类的构造函数的变形版,调用时相当于直接构造了一个类,但是毕竟不是类,导致this的不明确,所以要用是,需要自己指定this是针对谁。
换个角度说,类就是一个对象加同名的0构造函数,再new时,实际上是通过类的机制,调用里面那个构造函数
函数的重载
函数尽可能的用类型约束和泛型,不要重载
在TypeScript中,函数重载是指为同一个函数提供多个函数类型定义,以便在不同的参数类型或参数个数下可以有不同的行为。
重载的函数定义由多个函数签名组成,每个函数签名都描述了函数的参数类型和返回类型。当调用这个函数时,TypeScript会根据提供的参数类型或参数个数来选择合适的函数重载。
以下是一个示例,演示如何使用函数重载:
function foo(x: number): string;
function foo(x: string): number;
//真正发挥作用的就最后哦一个,前面的是用来提供类型提示的
function foo(x: any): any {
if (typeof x === 'number') {
return x.toString();
} else if (typeof x === 'string') {
return parseInt(x, 10);
}
}
console.log(foo(123)); // 输出 "123"
console.log(foo('456')); // 输出 456
在上面的例子中,我们定义了两个函数签名,分别接受一个 number 类型的参数并返回一个 string 类型的结果,以及一个 string 类型的参数并返回一个 number 类型的结果。在函数体内部,我们根据参数的类型来进行不同的处理,并返回相应的结果。
注意,函数重载只存在于类型层面,在编译后的 JavaScript 代码中,并不会有多个函数定义。重载只是在编译器中进行类型检查和类型推断时使用的工具。
TS的类
类的思想就是面向对象的思想
在TypeScript中,类是一种数据结构,它用于创建具有相同属性和方法的对象。类可以包含属性(即数据)和方法(即函数),并通过实例化来创建对象。类使用关键字“class”来定义,可以通过继承来扩展其他类的属性和方法。
以下是一个使用类定义的示例:
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
const john = new Person("John", 25);
john.sayHello(); // 输出: Hello, my name is John and I am 25 years old.
静态属性(类属性)和成员属性(实例属性)
静态属性是属于类本身的属性,而不是属于实例化对象的属性。它们使用static关键字进行声明。
成员属性是属于实例化对象的属性,每个实例化对象都有自己的成员属性。
以下是一个示例代码,展示了静态属性和成员属性的使用:
class MyClass {
static staticProperty: number = 10; // 静态属性
property: string; // 成员属性
constructor(property: string) {
this.property = property;
}
static staticMethod() {
console.log("静态方法");
console.log(MyClass.staticProperty); // 访问静态属性
}
memberMethod() {
console.log("成员方法");
console.log(this.property); // 访问成员属性
}
}
console.log(MyClass.staticProperty); // 访问静态属性
//10
MyClass.staticMethod(); // 调用静态方法
//静态方法 10
const obj = new MyClass("成员属性");
console.log(obj.property); // 访问成员属性
//成员属性
obj.memberMethod(); // 调用成员方法
//成员方法 成员属性
在这个例子中,`staticProperty`是一个静态属性,可以通过类名直接访问,也可以在静态方法中使用。静态随着类的创建而创建
`property`是一个成员属性,只能通过实例化对象访问,也只能在成员方法中使用。
# 继承
在TypeScript中,可以使用`extends`关键字来实现类的继承。继承允许一个类继承另一个类的属性和方法,并且可以在子类中添加新的属性和方法。
下面是一个示例:
```typescript
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number = 0) {
console.log(`${this.name} moved ${distance} meters.`);
}
}
class Dog extends Animal {
bark() {
console.log(`${this.name} barked.`);
}
}
let dog = new Dog("Buddy");
dog.move(10); // 输出: Buddy moved 10 meters.
dog.bark(); // 输出: Buddy barked.
在这个示例中,Dog类继承了Animal类,并通过extends关键字来指明继承关系。Dog类添加了一个新的方法bark。
通过创建一个Dog类的实例,我们可以调用继承自Animal类的move方法以及Dog类自己的bark方法。
需要注意的是,子类在继承父类时可以覆盖父类的方法或属性,也可以在自己的构造器中调用父类的构造器。在TypeScript中,可以使用extends关键字来实现类的继承。继承允许一个类继承另一个类的属性和方法,并且可以在子类中添加新的属性和方法。
下面是一个示例:
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number = 0) {
console.log(`${this.name} moved ${distance} meters.`);
}
}
class Dog extends Animal {
bark() {
console.log(`${this.name} barked.`);
}
}
let dog = new Dog("Buddy");
dog.move(10); // 输出: Buddy moved 10 meters.
dog.bark(); // 输出: Buddy barked.
在这个示例中,Dog类继承了Animal类,并通过extends关键字来指明继承关系。Dog类添加了一个新的方法bark。
通过创建一个Dog类的实例,我们可以调用继承自Animal类的move方法以及Dog类自己的bark方法。
需要注意的是,子类在继承父类时可以覆盖父类的方法或属性,也可以在自己的构造器中调用父类的构造器。
super关键字
- 当子类继承父类时,可以使用
super来调用父类的构造函数、方法和属性。 - 在构造函数中,使用
super()来调用父类的构造函数,以便在子类中初始化父类的成员。 - 在方法中,使用
super.methodName()来调用父类的方法。 - 在属性中,使用
super.propertyName来访问父类的属性。
示例:
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
eat() {
console.log('Animal is eating.');
}
}
class Dog extends Animal {
breed: string;
constructor(name: string, breed: string) {
super(name); // 调用父类的构造函数
this.breed = breed;
}
bark() {
super.eat(); // 调用父类的eat方法
console.log('Dog is barking.');
}
}
const dog = new Dog('Max', 'Labrador');
dog.bark();
输出:
Animal is eating.
Dog is barking.
this关键字
this在TypeScript中表示当前对象或实例。- 在类的方法中,使用
this来引用当前对象的属性和方法。 - 当在类的方法中遇到与类属性同名的局部变量时,可以使用
this来区分。
示例:
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello() {
console.log(`Hello, my name is ${this.name}.`);
}
changeName(newName: string) {
this.name = newName;
}
}
const person = new Person('John');
person.sayHello(); // 输出:Hello, my name is John.
person.changeName('Jane');
person.sayHello(); // 输出:Hello, my name is Jane.
注意:在箭头函数中,this关键字的行为与普通函数不同。在箭头函数中,this指向的是定义它的上下文,而不是调用它的对象。supur和this是两个在TypeScript中使用的关键字。
接口
接口是一种用于描述对象结构的规范,它定义了对象应该具有的属性和方法。接口可以被类实现,表示类遵循该接口定义的规范。接口使用关键字“interface”来定义。
以下是一个使用接口定义的示例:
interface Animal {
name: string;
age: number;
sound: string;
makeSound(): void;
}
//在TypeScript中,implements关键字用于实现接口。接口是定义一组属性和方法的规范,而implements关键字可以使一个类满足接口的要求。
class Dog implements Animal {
name: string;
age: number;
sound: string;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
this.sound = "Woof!";
}
makeSound() {
console.log(this.sound);
}
}
const dog = new Dog("Buddy", 3);
dog.makeSound(); // 输出: Woof!
在上面的示例中,Animal接口定义了Animal对象应该具有的属性和方法。Dog类实现了Animal接口,表示Dog类遵循Animal接口定义的规范。
抽象类和派生类
在TypeScript中,抽象类是一种被设计用于被其他类继承的基类。抽象类本身不能被实例化,只能被其他类继承。抽象类可以包含抽象方法、普通方法和属性。
抽象方法是一种在抽象类中定义的没有具体实现的方法,它只能被派生类实现。派生类需要实现抽象类中的所有抽象方法才能被实例化。抽象方法使用abstract关键字进行定义。
派生类是基于抽象类或其他类定义的类。它可以通过extends关键字继承抽象类的属性和方法,并可以重写或扩展这些属性和方法。派生类可以用来创建新的对象实例。
下面是一个使用抽象类和派生类的例子:
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log("Moving...");
}
}
class Dog extends Animal {
makeSound(): void {
console.log("Woof!");
}
}
let dog = new Dog();
dog.makeSound(); // 输出: Woof!
dog.move(); // 输出: Moving...
在上面的例子中,Animal类是一个抽象类,其中包含一个抽象方法makeSound()和一个普通方法move()。Dog类继承了Animal类,并实现了makeSound()方法。我们可以实例化Dog类并调用其方法。
需要注意的是,抽象类中的抽象方法必须在派生类中实现,否则会编译错误。此外,抽象类不能直接实例化,只能被继承。
类的的构造签名
在上面的示例中,我们定义了一个接口Person来表示一个人的类型,其中有两个属性name和age。然后,我们定义了一个构造签名接口PersonConstructor,它表示一个构造函数,参数name和age,返回一个Person对象。
最后,我们使用构造签名创建了一个实例PersonClass,并将其赋值给PersonConstructor类型的变量PersonClass。然后,我们使用new关键字创建出一个Person对象,并将其赋值给person变量。最后,我们打印出person对象的值。平时的类型声明针对属性,方法。这构造签名针对完整待实例化的类
interface Person {
name: string;
age: number;
}
interface PersonConstructor {
new(name: string, age: number): Person;
}
// 使用构造签名创建一个实例
const PersonClass: PersonConstructor = class PersonClass implements Person {
constructor(public name: string, public age: number) {}
}
const person: Person = new PersonClass("John", 25);
console.log(person); // { name: 'John', age: 25 }
对比理解
interface Person {
name: string;
age: number;
}
interface PersonConstructor {
new(name: string, age: number): Person;
}
// 使用构造签名创建一个实例
class PersonClass implements Person {
constructor(public name: string, public age: number) {
this.name = name;
this.age = age;
}
}
function person(ctor:PersonConstructor) {
return new ctor("John", 25);
}
const person1 = person(PersonClass);
console.log(person1); // { name: 'John', age: 25 }
联合类型和交叉类型及断言
联合类型
TS的联合类型指的是一种将多个类型组合在一起的类型。使用联合类型,我们可以将多个不同类型的值赋值给一个变量或参数。使用 | 符号将多个类型组合在一起即可创建联合类型。
例如,我们可以定义一个变量 x,它可以是 number 或 string 类型的值:
let x: number | string;
x = 10; // 可以赋值为 number 类型的值
x = "hello"; // 可以赋值为 string 类型的值
在上面的例子中,x 可以接受 number 类型的值和 string 类型的值,因为它的类型被定义为 number | string。
在使用联合类型的时候,我们可以使用类型保护来确定变量的具体类型,例如使用 typeof 和 instanceof 运算符。
function foo(x: number | string) {
if (typeof x === "number") {
console.log(x + 10);
} else if (typeof x === "string") {
console.log(x.toUpperCase());
}
}
foo(10); // 输出 20
foo("hello"); // 输出 HELLO
联合类型在 TS 中经常用于描述某个变量或参数可以接受多种类型的值的情况,提高了代码的灵活性和可重用性。
交叉类型
交叉类型(Intersection Types)是TypeScript中的一种类型操作符,表示可以同时具有多个类型的值。使用交叉类型可以将多个类型合并为一个类型,并且只有同时满足这些类型的值才能符合交叉类型的定义。
例如,假设有两个类型A和B:
type A = {
propA: string;
};
type B = {
propB: number;
};
可以使用交叉类型将A和B合并为一个类型C:
type C = A & B;
// C类型的值必须具有A和B的属性
const c: C = {
propA: "Hello",
propB: 123
};
在上面的例子中,类型C表示同时具有A和B的属性的类型。因此,变量c的值必须具有propA和propB属性,并且分别满足string和number类型的要求。
交叉类型可以用于合并任意数量的类型,并且可以与其他类型操作符(如联合类型和类型别名)一起使用,以创建更复杂的类型定义。
类型断言
在 TypeScript 中,类型断言是一种方式,可以告诉编译器变量的类型。有两种方式可以进行类型断言。
- 使用尖括号语法:
let someValue: any = "hello world";
let strLength: number = (<string>someValue).length;
- 使用 as 关键字:
let someValue: any = "hello world";
let strLength: number = (someValue as string).length;
这两种方式是等价的,你可以选择适合你个人喜好的方式来进行类型断言。需要注意的是,类型断言仅仅是编译时的检查,并不会影响运行时的行为。在进行类型断言时要确保变量的实际类型与断言的类型是兼容的,否则可能会导致编译错误或运行时异常。
3.使用感叹号
在 TypeScript 中,"!" 和 "?" 是用于类型声明和类型检查的符号。
"!" 符号表示一个值可能为 null 或 undefined,但是在使用之前需要进行非空断言。例如:
let str: string | null = "Hello world!";
console.log(str!.length); // 非空断言,告诉编译器该值不为空
str = null;
console.log(str!.length); // 在这种情况下会抛出运行时错误
"?" 符号用于表示一个属性或方法是可选的,可能存在也可能不存在。例如:
interface Person {
name: string;
age?: number;
}
const person1: Person = { name: "Alice" }; // age 属性是可选的
const person2: Person = { name: "Bob", age: 20 }; // age 属性是必需的
console.log(person1.age); // undefined
console.log(person2.age); // 20
类型别名
在TypeScript中,可以使用类型别名来为已有的类型创建一个新的名称。类型别名通过使用关键字type来定义。
type相当于抽象类
以下是一些常见的示例:
type Age = number;
type Name = string;
type Person = {
name: Name,
age: Age
};
const person: Person = {
name: "John",
age: 25
};
上面的代码中,我们通过类型别名为number类型创建了一个新的名称Age,为string类型创建了一个新的名称Name,以及为一个包含name和age属性的对象类型创建了一个新的名称Person。然后,我们使用这些类型别名来定义一个名为person的变量。
TS的接口interface
在TypeScript中,接口(Interface)是一种用于定义对象的形状(Shape)和行为的方式。接口可以包含属性和方法的定义,可以用于描述对象的结构。
下面是一些常见的用法和示例:
定义一个简单的接口
//普通的参数直接在用的时候声明类型,如果是对象这种的,直接在外面声明接口,再类型约束
interface Person {
name: string;
age: number;
}
let person: Person = {
name: "Alice",
age: 30,
// 不能添加其他属性,否则会报错
};
接口用于函数的类型限制
interface MathFunc {
(x: number, y: number): number;
}
let add: MathFunc = function(x: number, y: number): number {
return x + y;
};
接口继承其他接口
interface Person {
name: string,
age: number,
}
interface Student extends Person {
grade: number;
}
let student: Student = {
name: "Bob",
age: 20,
grade: 80,
};
type链接符扩展
type相当于抽象类
type Person ={
name: string,
age: number,
}
type Student =Person& {
grade: number;
}
let student: Student = {
name: "Bob",
age: 20,
grade: 80,
};
同名接口可以重合接口
注意type是不能同时存在两个同名的。 type相当于抽象类
interface Student{
grade: number
}
interface Student{
name: string;
age: number;
}
let student: Student = {
name: "Bob",
age: 20,
grade: 80,
};
接口可以使用可选属性
interface Config {
color?: string;
width?: number;
}
function createButton(config: Config): HTMLButtonElement {
// ...
}
let button = createButton({ color: "red" });
接口的索引签名
在 TypeScript 中,索引签名(Index Signatures)允许我们定义可以通过索引访问的属性类型。索引签名使用[]来表示。这样可以在不确定数据个数的情况下,动态定义类型。[index: string]代表index是一个string,如果在数组中可能是索引值,即为number
索引签名的语法如下:
interface SomeInterface {
[index: string]: any;
}
这里的index可以是string也可以是number,代表了索引的类型。
索引签名可以用来定义对象或类的属性类型,使其具有动态属性。如下接口,age必须是 number类型,其他可以是string或则number:
interface User {
age: number,
[key: string]: string | number;
}
const user: User = {
name: "John",
age: 25,
address: "123 Street",
};
console.log(user.name); // "John"
console.log(user.age); // 25
console.log(user.address); // "123 Street"
上面的示例中,我们定义了User接口,并使用索引签名使其具有任意字符串类型的属性。这就允许了User对象有任意字符串类型的属性。
除了字符串索引签名,我们还可以使用数字索引签名来定义数组类型。例如:
interface StringArray {
[index: number]: string;
}
const arr: StringArray = ["Hello", "World"];
console.log(arr[0]); // "Hello"
console.log(arr[1]); // "World"
上面的示例中,我们定义了StringArray接口,并使用了数字索引签名来定义数组类型。这样,我们就可以通过索引访问数组中的元素。
TS 支持 string 和 number 索引类型,可以同时使用两种类型的索引,但 number 索引的值(value)类型必须是 string 索引值(value)类型的子类型。 这是因为 JavaScript 语言规定:对象的键名一律为字符串。使用索引获取对象时,非字符串索引会被自动转为字符串。因为数组本身是对象,所以数组的索引(下标)实际上是字符串。
接口的只读修饰符
在TypeScript的接口中,可以使用readonly关键字来创建只读属性。只读属性表示只能在对象创建时被赋值,不能被修改。
interface Person {
readonly name: string;
readonly age: number;
}
let person: Person = {
name: "John",
age: 30
};
console.log(person.name); // "John"
console.log(person.age); // 30
// 以下代码会报错,因为name和age是只读属性
person.name = "Tom";
person.age = 40;
在上面的例子中,name和age属性都被声明为只读属性。当我们创建了Person对象后,就不能修改name和age属性的值了。如果试图修改只读属性的值,TypeScript编译器会报错。
需要注意的是,只读属性只能在对象创建时被赋值,一旦被赋值后就不能再被改变。这意味着,只读属性必须在对象创建时就有一个初始值。如果尝试在对象创建后修改只读属性的值,编译器将会报错。
readonly vs const
最简单判断该用readonly还是const的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用 const,若做为属性则使用readonly。
const只是不能改栈中地址,但是如果是数组这种的引用类型,虽然不能改变数组,但是依旧可以改变数组中的值
TypeScript具有ReadonlyArray<T>类型,它与Array<T>相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!
上面代码的最后一行,可以看到就算把整个ReadonlyArray赋值到一个普通数组也是不可以的。 但是你可以用类型断言重写:
a = ro as number[];
类型缩小
在 TypeScript 中,有几种方法可以将类型缩小为更具体的类型,其中包括:
类型断言
使用 as 关键字将变量的类型指定为更具体的类型
let value: unknown = 5;
let num: number = (value as number) + 10;
条件判断:
包括==,===,!=,!===,<,>,a?b:c
let num: number = 5;
let isPositive: boolean = num > 0;
typeof 操作符
使用 typeof 操作符获取变量的类型并进行判断
判断的有:string,number,booleam,undefined,object,function,bigint,symbol
function getLength(value: string | number) {
if (typeof value === 'string') {
return value.length;
}
return value;
}
instanceof 操作符
使用 instanceof 操作符检查一个对象是否属于某个类的实例,例如:
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
function greet(person: Person | string) {
if (person instanceof Person) {
return `Hello, ${person.name}!`;
}
return `Hello, ${person}!`;
}
in 操作符
使用 in 操作符判断一个属性是否存在于一个对象中
interface Dog {
name: string;
breed: string;
}
interface Cat {
name: string;
breed: string;
}
function print(animal: Dog | Cat) {
if ('breed' in dog) {
console.log(`This dog is ${dog.name}.`);
} else {
console.log(`This dog is ${cat.name}.`);
}
}
这些方法都可以根据不同的条件缩小一个变量的类型,使其更具体。
is类型谓词
在TypeScript中,is是一种类型谓词(type predicate)语法,用于在运行时判断一个值的类型是否是某个给定类型。
语法:
parameterName is Type
示例:
function isString(value: unknown): value is string {
return typeof value === 'string';
}
function doSomething(value: unknown) {
if (isString(value)) {
console.log(value.toLowerCase());
} else {
console.log('Value is not a string');
}
}
doSomething('Hello'); // 输出:hello
doSomething(123); // 输出:Value is not a string
在上面的示例中,isString函数判断一个值是否是字符串类型。如果判断为真,则value的类型被缩小为string类型,因此在if语句中可以安全地调用字符串方法toLowerCase。如果判断为假,则else部分会被执行。
通过使用is语法,我们可以在编译时进行类型检查,使代码更加健壮和可靠。
联合类型简单的案例
不理想的示例 这个案例看上去没有问题,但是radius和sideLength,是可选的,虽然在函数中用!强制断言了,但是用户还是可能没给。随之就会出错。
type Shape = {
kind: 'circle'|'square';
radius?: number
sideLength?: number
};
// 求面积
function getArea(shape: Shape): number {
if (shape.kind === 'circle') {
return Math.PI * shape.radius! ** 2;
} else {
return shape.sideLength! ** 2;
}
}
// 调用
getArea({ kind: 'circle', radius: 10 });
正确示例
// 定义一个圆形的type
type Circle = {
kind: 'circle'
radius: number;
};
// 定义一个正方形的type
type Square = {
kind: 'square'
sideLength: number;
}
// 求面积
type Shape = Circle | Square
// 求面积
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2
case 'square':
return shape.sideLength ** 2
default:
//这是如果输入一个三角形,因为超出程序设定,所以就会报never的错
{
const _exhaustiveCheck: never = shape
throw new Error(`不能使用该类型: ${JSON.stringify(_exhaustiveCheck)}`)
}
}
}
// 使用函数
const circle: Shape = { kind: 'circle', radius: 5 }
const square: Shape = { kind: 'square', sideLength: 4 }
console.log(getArea(circle)) // 输出 78.53981633974483
console.log(getArea(square)) // 输出 16
布尔真值缩小
条件判断:
let num: number = 5;
let isPositive: boolean = num > 0;
在这个例子中,通过判断num是否大于0,可以将isPositive缩小为true或false。
布尔赋值
boolean("hello")
双叹号
第一个叹号会对字符串取反,将其转换成了布尔类型,第二个叹号再取反,将值再转换回去
!!"world"