如何在 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
同时包含 date
和 text
。但是当您将 cell
分配为 CellA
或 CellB
类型时,打字稿会抱怨,因为每种类型都缺少其中一个属性。
你能只分配可选属性吗?像这样:
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
应该是 CellA
和 CellB
的组合,那么您可以使 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 非常相似。如果 CellA
和 CellB
应该共享一些属性,那么它们中的一个可以扩展另一个,或者它们可以扩展一个公共接口。例如:
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 值。
我正在尝试下面的代码,但不确定我是否可以告诉打字稿从任一接口假定类型
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
同时包含 date
和 text
。但是当您将 cell
分配为 CellA
或 CellB
类型时,打字稿会抱怨,因为每种类型都缺少其中一个属性。
你能只分配可选属性吗?像这样:
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
应该是 CellA
和 CellB
的组合,那么您可以使 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 非常相似。如果 CellA
和 CellB
应该共享一些属性,那么它们中的一个可以扩展另一个,或者它们可以扩展一个公共接口。例如:
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 值。