乐闻世界logo
搜索文章和话题

How to check interface type with typescript

7 个月前提问
3 个月前修改
浏览次数51

6个答案

1
2
3
4
5
6

在TypeScript中,类型检查是自动发生的,并且是语言的核心特性之一。当你定义了一个interface,TypeScript会在编译时进行类型检查,以确保你遵守了该接口定义的结构。

举个例子来说,假设我们有以下的interface

typescript
interface IUser { name: string; age: number; isActive: boolean; }

当你尝试创建一个IUser类型的对象时,TypeScript编译器会自动检查这个对象是否匹配IUser接口。例如:

typescript
let user: IUser = { name: 'Alice', age: 30, isActive: true };

在这个例子中,user对象遵循了IUser的结构,因此它会成功编译。但是,如果你尝试添加一个不在interface中的属性,或者遗漏了一个属性,TypeScript编译器会抛出一个错误:

typescript
let user: IUser = { name: 'Bob', // 'age' 属性丢失将会导致编译错误 isActive: true };

会产生类似以下的错误:

shell
Property 'age' is missing in type '{ name: string; isActive: boolean; }' but required in type 'IUser'.

除此以外,你还可以在函数中使用接口来进行类型检查。例如:

typescript
function greetUser(user: IUser) { console.log(`Hello, ${user.name}!`); } greetUser({ name: 'Charlie', age: 25, isActive: false }); // 正确使用 greetUser({ name: 'Dave' // 缺少其他属性将导致错误 });

对于函数greetUser,调用它时传入的参数需要符合IUser接口的结构,否则TypeScript编译器会提示错误。

总的来说,在TypeScript中,interface类型检查是静态的,发生在编译时,而不是运行时。编译器会根据你定义的接口来检查代码中的结构和类型,确保类型的正确性和一致性。这个特性在大型项目中特别有用,因为它可以在早期就发现潜在的错误和问题。

2024年6月29日 12:07 回复

You can achieve what you want without the instanceof keyword as you can write custom type guards now:

shell
interface A { member: string; } function instanceOfA(object: any): object is A { return 'member' in object; } var a: any = {member: "foobar"}; if (instanceOfA(a)) { alert(a.member); }

Lots of Members

If you need to check a lot of members to determine whether an object matches your type, you could instead add a discriminator. The below is the most basic example, and requires you to manage your own discriminators... you'd need to get deeper into the patterns to ensure you avoid duplicate discriminators.

shell
interface A { discriminator: 'I-AM-A'; member: string; } function instanceOfA(object: any): object is A { return object.discriminator === 'I-AM-A'; } var a: any = {discriminator: 'I-AM-A', member: "foobar"}; if (instanceOfA(a)) { alert(a.member); }
2024年6月29日 12:07 回复

In TypeScript 1.6, user-defined type guard will do the job.

shell
interface Foo { fooProperty: string; } interface Bar { barProperty: string; } function isFoo(object: any): object is Foo { return 'fooProperty' in object; } let object: Foo | Bar; if (isFoo(object)) { // `object` has type `Foo`. object.fooProperty; } else { // `object` has type `Bar`. object.barProperty; }

And just as Joe Yang mentioned: since TypeScript 2.0, you can even take the advantage of tagged union type.

shell
interface Foo { type: 'foo'; fooProperty: string; } interface Bar { type: 'bar'; barProperty: number; } let object: Foo | Bar; // You will see errors if `strictNullChecks` is enabled. if (object.type === 'foo') { // object has type `Foo`. object.fooProperty; } else { // object has type `Bar`. object.barProperty; }

And it works with switch too.

2024年6月29日 12:07 回复

How about User-Defined Type Guards? https://www.typescriptlang.org/docs/handbook/advanced-types.html

shell
interface Bird { fly(); layEggs(); } interface Fish { swim(); layEggs(); } function isFish(pet: Fish | Bird): pet is Fish { //magic happens here return (<Fish>pet).swim !== undefined; } // Both calls to 'swim' and 'fly' are now okay. if (isFish(pet)) { pet.swim(); } else { pet.fly(); }
2024年6月29日 12:07 回复

typescript 2.0 introduce tagged union

Typescript 2.0 features

shell
interface Square { kind: "square"; size: number; } interface Rectangle { kind: "rectangle"; width: number; height: number; } interface Circle { kind: "circle"; radius: number; } type Shape = Square | Rectangle | Circle; function area(s: Shape) { // In the following switch statement, the type of s is narrowed in each case clause // according to the value of the discriminant property, thus allowing the other properties // of that variant to be accessed without a type assertion. switch (s.kind) { case "square": return s.size * s.size; case "rectangle": return s.width * s.height; case "circle": return Math.PI * s.radius * s.radius; } }
2024年6月29日 12:07 回复

Type guards in Typescript:

TS has type guards for this purpose. They define it in the following manner:

Some expression that performs a runtime check that guarantees the type in some scope.

This basically means that the TS compiler can narrow down the type to a more specific type when it has sufficient information. For example:

shell
function foo (arg: number | string) { if (typeof arg === 'number') { // fine, type number has toFixed method arg.toFixed() } else { // Property 'toFixed' does not exist on type 'string'. Did you mean 'fixed'? arg.toFixed() // TSC can infer that the type is string because // the possibility of type number is eliminated at the if statement } }

To come back to your question, we can also apply this concept of type guards to objects in order to determine their type. To define a type guard for objects, we need to define a function whose return type is a type predicate. For example:

shell
interface Dog { bark: () => void; } // The function isDog is a user defined type guard // the return type: 'pet is Dog' is a type predicate, // it determines whether the object is a Dog function isDog(pet: object): pet is Dog { return (pet as Dog).bark !== undefined; } const dog: any = {bark: () => {console.log('woof')}}; if (isDog(dog)) { // TS now knows that objects within this if statement are always type Dog // This is because the type guard isDog narrowed down the type to Dog dog.bark(); }
2024年6月29日 12:07 回复

你的答案