创建打字稿类型“接口之一”

时间:2019-11-06 15:05:33

标签: javascript typescript

我需要创建其行为将在给定接口之一之间进行选择的类型。

请注意:在下面的示例中,someTask是从服务器返回的数据!代码中没有实际启动它! 例如:

interface TaskA { foo: string }

interface TaskB { fee: string }

type Task = TaskB | TaskB 

const task:Task = someTask

console.log(task.fee)

console.log函数会对我大喊:

  

类型“任务”上不存在属性“费用”。

我在这里做什么错了?

3 个答案:

答案 0 :(得分:1)

Typescript中使用的术语“联合”和“交集”在数学上是正确的,但是当应用于对象/界面时仍然有些混乱。两个接口A和B的并集是A或B所有对象的一种类型。为了可靠地使用这种类型,编译器必须确保仅使用A和B中都存在的键。换句话说,对象的并集仅包含其键的交集。相反,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

由于您的对象没有任何公共键,因此它们的并集本质上是一个空对象({never: any})。它不能具有任何属性。

您可以做的是引入一个 tagged 联合,其中所有成员类型都具有一个公共属性(“标记”),使编译器可以区分它们。示例:

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);
}

在这里,编译器知道,如果typefirst,则该对象应该具有one属性。

答案 1 :(得分:0)

应该可以。

确保在对象中定义了属性“ fee”。

enter image description here

答案 2 :(得分:0)

如果不能确定从服务器获取的对象是任务,则应为TaskA或TaskB添加用户定义类型防护。这样,在这种情况下,您可以确定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');
}