你能在 Typescript 中执行智能类型吗?

Leo*_*ici 2 types typescript

假设我有这样的界面:

export interface Promotion {
  title: string;
  image?: string;
  description?: string;
  startDate?: string;
  endDate?: string;
  type: 'discountWithImage' | 'discount' | 'countdown';
}
Run Code Online (Sandbox Code Playgroud)

现在我的问题是,因为我现在确定如果键type等于'countdown'则 endDate 和 startDate 将不会未定义。有没有办法在打字稿中做到这一点?所以像这样:

export interface Promotion {
  title: string;
  image?: string;
  description?: string;
  startDate?: string;
  endDate: Promotion.type === 'countdown' ? string : undefined;
  type: 'discountWithImage' | 'discount' | 'countdown';
}
Run Code Online (Sandbox Code Playgroud)

在Playground上解释的例子

Ale*_*yne 5

您可以使用泛型类型参数来完成此操作。把它想象成你传递给你的类型的参数。

type PromotionType = 'discountWithImage' | 'discount' | 'countdown';

export interface Promotion<T extends PromotionType> {
  title: string;
  image?: string;
  description?: string;
  startDate?: string;
  endDate: T extends 'countdown' ? string : undefined;
  type: T;
}
Run Code Online (Sandbox Code Playgroud)

你会像这样使用它:

declare const testA: Promotion<'discount'>
const endDateA = testA.endDate // undefined

declare const testB: Promotion<'countdown'>
const endDateB = testB.endDate // string
Run Code Online (Sandbox Code Playgroud)

您甚至可以使用泛型函数来为您传递该类型参数:

function getPromotion<T extends PromotionType>(type: T): Promotion<T> {
  return {} as Promotion<T> // Not a real implementation
}

const resultA = getPromotion('discount').endDate // undefined
const resultB = getPromotion('countdown').endDate // string
Run Code Online (Sandbox Code Playgroud)

操场


或者,您可以根据 的值定义一个接口稍有不同的可区分联合type

// One interface for common values that all promotions share
export interface PromotionCommon {
  title: string;
  image?: string;
  description?: string;
  startDate?: string;
}

// The discount promotion type has no end date and discount type.
export interface PromotionDiscount extends PromotionCommon {
  endDate: undefined;
  type: 'discountWithImage' | 'discount';
}

// The countdown promotion type has an end date and a countdown type.
export interface PromotionCountdown extends PromotionCommon {
  endDate: string;
  type: 'countdown';
}

// The final Promotion type can be either a discount or a countdown
type Promotion = PromotionDiscount | PromotionCountdown


declare const promotion: Promotion // pretend we have a promotion of unknown promotion type
if (promotion.type === 'countdown') {
  const endDate = promotion.endDate // type: string
} else {
  const endDate = promotion.endDate // type: undefined
}
Run Code Online (Sandbox Code Playgroud)

操场