简体   繁体   English

创建 typescript 类型“接口之一”

[英]creating typescript type “one of interfaces”

I need to create type which it's behaviour would be choosing between one of given interfaces.我需要创建其行为将在给定接口之一之间进行选择的类型。

Please note: in the example below someTask is data returned from the server: There is no actual initiation of it in the code!请注意:下例中someTask是从服务器返回的数据:代码中并没有实际启动它! For example:例如:

interface TaskA { foo: string }

interface TaskB { fee: string }

type Task = TaskB | TaskB 

const task:Task = someTask

console.log(task.fee)

The console.log function will yell at me: console.log function 会冲我大喊:

Property 'fee' does not exist on type 'Task'. “任务”类型上不存在属性“费用”。

What am I doing wrong here?我在这里做错了什么?

The terms "union" and "intersection" as used in Typescript are mathematically correct, but still a bit confusing when applied to objects/interfaces. Typescript 中使用的术语“联合”和“交集”在数学上是正确的,但在应用于对象/接口时仍然有点混乱。 A union of two interfaces A and B is a type for all objects which are A or B. To work with such type reliably, the compiler has to be sure that only keys that are present in both A and B are used.两个接口 A 和 B 的联合是所有对象 A 或 B 的类型。要可靠地使用这种类型,编译器必须确保只使用 A 和 B 中都存在的键。 In other words, a union of objects contains only an intersection of their keys.换句话说,对象的联合只包含它们的键的交集。 Conversely, an intersection of A and B, A & B , contains all keys from both A and B, that is, an intersection of interfaces is a union of their keys:相反,A 和 B 的交集A & B包含来自 A 和 B 的所有键,也就是说,接口的交集是它们键的并集:

type A = { a: 1, b: 2, x: 3 }
type B = { a: 1, b: 2, y: 3 }

type U = keyof (A | B)  // a|b
type I = keyof (A & B)  // a|b|x|y

Since your objects don't have any common keys, their union is essentially an empty object ( {never: any} ).由于您的对象没有任何公用键,因此它们的联合本质上是一个空的 object ( {never: any} )。 It cannot have any properties.它不能有任何属性。

What you can do, is to introduce a tagged union, in which all member types have a common property ("tag") which enables the compiler to differentiate between them.您可以做的是引入一个标记联合,其中所有成员类型都有一个公共属性(“标记”),使编译器能够区分它们。 Example:例子:

type SomeTask<T, P extends {}> = { type: T } & P

type TaskOne = SomeTask<'first', {
    one: number
}>

type TaskTwo = SomeTask<'second', {
    two: number
}>

type Task = TaskOne | TaskTwo;

function validateTask(t: Task) {
    if (t.type === 'first')
        console.log(t.one);
    if (t.type === 'second')
        console.log(t.two);
}

Here, the compiler knows, that if the type is first , then the object is expected to have the one property.在这里,编译器知道,如果typefirst ,那么 object 应该具有one属性。

It should work.它应该工作。

Make sure that you have defined property 'fee' in your object.确保您在 object 中定义了属性“费用”。

在此处输入图像描述

If you can't be sure that the object you get from your server is a Task, you should add a User Defined Type Guard for either TaskA or TaskB.如果您不能确定您从服务器获得的 object 是一个任务,您应该为 TaskA 或 TaskB 添加一个用户定义的类型保护。 That way you can be sure that someTask has fee as a property, in this case.在这种情况下,您可以确定someTask作为属性具有fee

interface TaskA { foo: string }

interface TaskB { fee: string }

type Task = TaskA | TaskB 

const task: Task = {fee: 'bar'};

console.log(isTaskA(task) ? task.foo : task.fee)

function isTaskA(value: TaskA | TaskB): value is TaskA {
    return value.hasOwnProperty('foo');
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM