如何在 Typescript 中添加多个断言

How to add multiple assertions in Typescript

我正在尝试下面的代码,但不确定我是否可以告诉打字稿从任一接口假定类型

export interface CellA {
    text: string;
}

export interface CellB {
    date: Date;
}

var cell = {};

const { date, text } = cell as (CellA | CellB);

console.log(date, text);

TS Error: Property 'date' does not exist on type 'CellA | CellB'.(2339)

我所追求的是让打字稿假设两个接口中都存在被破坏的变量。 TS Playground Example

如果可能,您可以扩展接口以接受任何其他值。例如:

export interface CellA {
    text?: string;
    [index: string]: any;
}

export interface CellB {
    date?: Date;
    [index: string]: any;
}

您这样做的方式是假设 cell 同时包含 datetext。但是当您将 cell 分配为 CellACellB 类型时,打字稿会抱怨,因为每种类型都缺少其中一个属性。

你能只分配可选属性吗?像这样:

interface CellType {
  date?: Date,
  text?: string
}

const { date, text } = cell as CellType

或者如果你真的想强制 cell 严格成为这些类型之一,我会在你定义变量时这样做:

interface CellA {
  text: string;
}

interface CellB {
  date: Date;
}

type CellType = CellA | CellB

const cell: CellType = { ... }

您的问题是 cell 可以是 CellA 类型,它没有 属性 date(因此您收到错误消息)。请注意,反之亦然,即 cell 也没有 属性 text

潜在的解决方案

1。使用交集类型

如果 cell 应该是 CellACellB 的组合,那么您可以使 cell 成为 CellA & CellB 类型。例如:

type CellC = CellA & CellB

const cell: CellC = { date: new Date(), text: 'Hello!' }

const { date, text }
console.log(date, text)

2。使用类型保护:Playground

如果您有一个值需要在运行时进行类型检查,您可以使用 user-defined type guard。例如,如果您有一个变量 cell,您在运行时不知道其类型:

const cell = { text: 'Hello!' } // unknown type

function isCellA(obj: any): obj is CellA {
    if(!obj) return false
    if(!obj.text) return false
    if(typeof obj.text !== 'string') return false

    return true
}

if(isCellA(cell)) {
    // at this point the compiler recognizes that cell
    // is of type CellA
    console.log(cell.text)
}

如果您需要在运行时进行类型检查,这可能是您的最佳选择。如果 cell 确实不是您预期的那样,它可以维护类型安全并帮助减少运行时错误。

3。扩展另一个接口

这个与解决方案 #1 非常相似。如果 CellACellB 应该共享一些属性,那么它们中的一个可以扩展另一个,或者它们可以扩展一个公共接口。例如:

interface Cell {
    date: Date
}

interface CellC extends Cell {
    text: string
}

4:React组件组成

如果您遇到此问题特别是 React(如您的评论所述),您可以考虑使用一个单独的组件,returns 是每种单元格类型的主要组件。 示例:

function ACell({ data }: { data: CellA } ) {
    return <Cell>{data.text}<Cell>
}

function BCell({ data }: { data: CellB } ) {
    return <Cell>{data.date.toString()}<Cell>
}

function Cell({ children }: { children: string }) {
    return <p>{children}</p>
}

也许这不符合您的特定用例,但类似的东西可能有助于分离组件之间每种类型的单元格的逻辑,并与第三个 Cell 组件共享公共逻辑。

一般情况下,如果您想在 Typescript 中切换 argument/variable 类型的控制流,您需要使用 discriminated union:

interface CellA {
  text: string,
  kind: "CellA",
}

interface CellB {
  date: Date,
  kind: "CellB",
}

type Cell = CellA | CellB;

function takesACell(cell: Cell) {
  switch(cell.kind) {
    case "CellA":
      console.log("Got an A");
      cell.text; // no type error
      break;
    case "CellB":
      console.log("Got a B");
      cell.date; // no type error
      break;
  }
}

编译器将此模式理解为您正在根据运行时类型进行调度,但它仍将提供 compile-time 类型安全性,因为您不能只传递 non-conforming 值。

Playground