likes
comments
collection
share

ts中接口的基本用法

作者站长头像
站长
· 阅读数 5

基本用法

const getFullName = ({
  firstName,
  lastName,
}: {
  // 指定这个参数的类型,因为他是一个对象,所以这里来指定对象中每个字段的类型
  firstName: string; // 指定属性名为firstName和lastName的字段的属性值必须为string类型
  lastName: string;
}) => {
  return `${firstName} ${lastName}`;
};

使用对象字面量类型来限定传入的对象的结构。对象字面量类型是一种特殊的类型,它允许我们指定对象中每个属性的名称和类型。

在编写代码的时候 TypeScript 提示给我们的错误信息。

ts中接口的基本用法

同时,可以使用了TypeScript中的接口来定义一个名为Info的接口,它包含了两个属性:firstName和lastName,它们的类型都是string。

接下来,使用这个接口来定义一个名为getFullName的函数。这个函数接受一个对象作为参数,这个对象必须符合Info接口的定义。

在定义一个接口时,你可以把它看作是一个代码块,里面包含了一条条声明语句,只不过声明的不是变量的值而是类型。这样做的好处是,你可以在接口中定义多个属性和方法,以及它们的类型,从而确保你的代码中使用的对象具有正确的属性和方法

interface Info {
  firstName: string;
  lastName: string;
}
const getFullName = ({ firstName, lastName }: Info) =>
  `${firstName} ${lastName}`;

定义可选类型

interface Vegetables {
  color?: string;
  type: string;
}

避免多余属性检查

类型断言

在这个接口中,Vegetables 描述了一个对象,该对象有两个属性:color(可选属性,表示颜色)和 type(必选属性,表示类型)。

interface Vegetables {
  color?: string;
  type: string;
}

这个函数接受一个类型为 Vegetables 的参数对象,并根据该对象的属性构造一个字符串,形如 "A red tomato" 或者 "A cucumber"。

const getVegetables = ({ color, type }: Vegetables) => {
  return `A ${color ? color + " " : ""}${type}`;
};

进行函数调用

getVegetables({
  type: "tomato",
  size: 12,
  price: 1.2
} as Vegetables);

在函数调用中,传入了一个对象字面量,该对象包含了 type、size 和 price 三个属性。由于 TypeScript 具有类型检查,当你尝试传入包含非接口定义属性的对象时,TypeScript 会发出警告。

为了解决这个问题,你可以使用类型断言(Type Assertion),将对象断言为特定的接口类型,告诉TypeScript编译器,你知道这个对象的结构符合接口的定义,不必发出警告。在上述例子中,可以使用 as Vegetables 将 vegetable 对象断言为 Vegetables 类型:

需要注意的是,这种做法是绕过了 TypeScript 的类型检查,如果确保传入的对象结构与接口兼容,就可以使用类型断言。但是,最好的方式是修改传入的对象,使其符合接口定义,以确保类型的一致性和代码的清晰性。

添加索引签名

在TypeScript中,索引签名(Index Signature)允许你定义一个对象可以包含任意属性(键)以及它们对应的值的类型。索引签名使用字符串或数字作为键,这样你就可以灵活地处理对象的属性。

interface Dictionary {
  [key: string]: string;
}

const dictionary: Dictionary = {
  apple: "果子",
  banana: "香蕉"
};

console.log(dictionary.apple); // 输出: "果子"

在上面的例子中,Dictionary 接口定义了一个字符串索引签名,表示该接口的对象可以包含任意字符串作为键,并且每个键对应的值的类型必须是字符串。

除此之外,还可以采用数字签名

interface NumberDictionary {
  [key: number]: string;
}

const numDictionary: NumberDictionary = {
  1: "One",
  2: "Two"
};

console.log(numDictionary[1]); // 输出: "One"

在这个例子中,NumberDictionary 接口定义了一个数字索引签名,表示该接口的对象可以包含任意数字作为键,并且每个键对应的值的类型必须是字符串。

还可以同时使用字符串索引签名和数字索引签名,这样对象可以同时支持字符串和数字作为键:

interface MixedDictionary {
  [key: string]: number;
  [index: number]: string;
}

const mixedDictionary: MixedDictionary = {
  "one": 1,
  2: "Two"
};

console.log(mixedDictionary.one); // 输出: 1
console.log(mixedDictionary[2]); // 输出: "Two"

上述例子中,MixedDictionary 接口定义了同时支持字符串和数字键的对象,允许你混合使用不同类型的键。但请注意,当使用混合索引签名时,需要确保字符串索引的值类型是数字索引的值类型的子类型,否则 TypeScript 编译器可能会发出警告。

因此也可以通过以下方式来避免类型检查

interface Vegetables {
  color: string;
  type: string;
  [prop: string]: any;
}
const getVegetables = ({ color, type }: Vegetables) => {
  return `A ${color ? color + " " : ""}${type}`;
};
getVegetables({
  color: "red",
  type: "tomato",
  size: 12,
  price: 1.2
});

我们定义了一个接口 Vegetables其中有两个固定属性:color 和 type,它们的类型分别为字符串。此外,接口还包含了一个索引签名,允许接口的实现对象包含任意属性(键)和它们的值,值的类型为 any。

我们定义了一个函数 getVegetables,它接收一个参数,这个参数的类型是符合 Vegetables 接口的。在这个函数中,我们可以通过解构语法获取参数对象的 color 和 type 属性,并且使用这些属性生成一个描述蔬菜的字符串。

const getVegetables = ({ color, type }: Vegetables) => {
  return `A ${color ? color + " " : ""}${type}`;
};

最后,我们调用 getVegetables 函数并传递一个对象作为参数。这个对象包含了 color、type 以及额外的属性 size 和 price。由于 Vegetables 接口包含了索引签名,所以额外的属性不会引发类型错误。

getVegetables({
  color: "red",
  type: "tomato",
  size: 12,
  price: 1.2
});

通过使用索引签名,我们可以在接口中定义一些固定属性,同时允许实现对象包含任意其他属性,从而实现更加灵活的数据结构定义。

类型兼容性

interface Vegetables {
  type: string;
}
const getVegetables = ({ type }: Vegetables) => {
  return `A ${type}`;
};

const option = { type: "tomato", size: 12 };
getVegetables(option);

在 TypeScript 中,当你将一个对象字面量直接传递给一个函数时,TypeScript 会进行对象字面量的上下文类型推断。这意味着 TypeScript 会根据函数参数的类型来推断对象字面量的类型。在这种情况下,TypeScript 会使用函数参数的类型作为对象字面量的类型,而不是直接使用字面量的类型。

在上述代码中,getVegetables 函数期望接收一个类型为 Vegetables 的参数。但是,当你传递 { type: "tomato", size: 12 } 时,它的类型被 TypeScript 推断为 { type: string; size: number; },因为 TypeScript 根据传递的对象字面量的结构来推断类型。由于 { type: string; size: number; } 不兼容于 Vegetables,所以 TypeScript 报错。

然而,如果你先将对象字面量赋值给一个变量 option,然后将 option 传递给 getVegetables 函数,TypeScript 会使用 getVegetables 函数参数的类型(也就是 Vegetables 类型)来推断 option 的类型,而不是根据对象字面量的结构来推断。在这种情况下,由于 option 的类型与 Vegetables 类型兼容,所以赋值是允许的,不会产生类型错误。

在 TypeScript 中,类型兼容性是基于结构子类型的。这意味着当一个类型的结构(即属性和方法的组合)在另一个类型中能够找到相应的结构时,我们就说这两个类型是兼容的。

在上下文类型推断的情境下,当我们将一个对象字面量赋值给一个变量,并且该变量作为函数的参数时,TypeScript 会根据函数参数的类型(在这个例子中是 Vegetables 类型)来推断对象字面量的类型。然后,TypeScript 会检查对象字面量的结构是否与目标类型(在这里是 Vegetables 类型)相匹配。

如果对象字面量的结构包含了目标类型中定义的所有属性,并且属性的类型也相匹配(或者是兼容的,比如字面量的属性类型是目标类型属性类型的子类型),那么这个对象字面量就被认为是兼容的。这种情况下,对象字面量的类型会被隐式地视为目标类型。

所以,在这个例子中,当我们将 { type: "tomato", size: 12 } 赋值给变量 option 时,TypeScript 根据 Vegetables 类型推断出 option 的类型为 { type: string; }。尽管 { type: string; } 和 Vegetables 的定义不完全一致,但在这个上下文中,它们是兼容的,因为 option 的结构是 { type: string; },它包含了 Vegetables 类型中定义的所有属性,且属性类型是兼容的(string 类型是 Vegetables 中 type 属性的子类型)。因此,赋值是允许的,不会产生类型错误。

这种行为在 TypeScript 中被称为上下文类型推断,它使得对象字面量的类型能够根据它们在代码中的上下文(比如作为函数参数)来灵活地确定。

只读属性

定义如下接口

interface Role {
  readonly 0: string;
  readonly 1: string;
}
const role: Role = {
  0: "super_admin",
  1: "admin"
};
role[1] = "super_admin"; // Cannot assign to '0' because it is a read-only property

在这个代码示例中,Role 类型被定义为一个只读的索引签名。这意味着 Role 类型的对象(在这里是 role 对象)中的属性是只读的,不允许被重新赋值。

在 TypeScript 中,如果一个对象属性被定义为只读(通过 readonly 修饰符),那么它就不能被重新赋值。在你的例子中,role 对象的属性 0 和 1 被定义为只读属性,因此不能被重新赋值。

所以,当你尝试执行 role[1] = "super_admin"; 这行代码时,TypeScript 编译器会报错,提示你不能对只读属性进行重新赋值。错误信息中的 "Cannot assign to '0' because it is a read-only property" 意味着无法对属性 0 进行赋值,因为它是只读属性。

readonlyconst 都用于创建不可修改的值,但它们有不同的应用场景和语境。

  • readonly 关键字用于对象的属性。当一个属性被标记为 readonly 时,该属性只能在对象初始化时被赋值,并且不能被修改。它通常用于确保对象的属性在对象创建后不会被改变。
  • const 关键字用于声明常量。常量是在声明时就确定了值,并且无法重新赋值。它通常用于定义一些固定的、不会改变的值,比如数学常数、固定配置等。

在 TypeScript 中,如果需要定义一个对象的属性,并且希望它在对象创建后不可被修改,你应该使用 readonly。这样可以确保对象的属性不会被意外修改,提高代码的可靠性和安全性。

如果你只是定义一个常量值,而不是对象的属性,你应该使用 const。这样可以让编译器知道这个值是一个常量,可以帮助你在后续的代码中发现潜在的错误。

选择使用 readonly 还是 const 取决于你的需求和代码的结构。在需要保护对象属性不被修改的情况下,使用 readonly。在需要定义常量值的情况下,使用 const。

函数类型

在TypeScript中,接口(Interfaces)不仅可以用来描述对象的形状,还可以用来定义函数类型。这样的接口被称为函数类型接口。函数类型接口定义了函数的参数类型和返回值类型。

interface AddFunction {
  (a: number, b: number): number;
}

let add: AddFunction;

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

let result = add(10, 20); // result will be 30

在这个例子中,我们定义了一个函数类型接口AddFunction,它描述了一个函数,该函数接受两个参数(两个数字)并返回一个数字。然后,我们声明了一个变量add,该变量的类型是AddFunction,并将一个满足该接口的函数赋值给它。最后,我们可以使用add变量来调用函数,传入两个数字,并得到返回值。

使用函数类型接口可以帮助我们定义函数的结构,使得代码更加清晰、可读,并提供了类型检查的好处。

转载自:https://juejin.cn/post/7299353265099456521
评论
请登录