网站搜索

typescript - 字面量联合类型的类型保护


我在 TypeScript 中有两种类型:

type PublicMethods = 'Time' | 'Assets' | 'AssetPairs' ;
type PrivateMethods = 'Balance' | 'TradeBalance';

我想使用相同的api函数来处理这些类型,但每种类型的行为都不同。大概像这样:

public api = (method: PublicMethods | PrivateMethods, params: any) => {

  // ...how do I create a type guard here?
  if(method instanceof PublicMethods) { // 😱 Doesn't like this!
    // ...
  }

}

我也尝试过像这样重载函数:

public api(method: PublicMethods | PrivateMethods, params: any, callback: Function);

public api(method: PublicMethods, params: any, callback: Function) {
   // ...implementation
}

public api(method: PrivateMethods, params: any, callback: Function) {
   // ...implementation
}

那个我也不喜欢。有什么建议吗?

答案#1

你可以这样创建类型保护:

const isPublicMethod = (value: any): value is PublicMethod => {
  return ['Time', 'Assets', 'AssetPairs'].includes(value);
};

然后在你的 API 集成函数中使用它:

public api = (method: PublicMethods | PrivateMethods, params: any) => {
  if(isPublicMethod(method)) {
    ...
  }
}

警告!请小心,因为如果 PublicMethod 的类型已更新而类型保护没有更新,TS 不会抛出错误。

为更好地处理此问题,请使用以下方法:

const PUBLIC_METHODS = ['Time', 'Assets', 'AssetPairs'] as const;

type PublicMethod = typeof PUBLIC_METHODS[number];

const isPublicMethod = (value: any): value is PublicMethod => {
  return PUBLIC_METHODS.includes(value);
};

答案#2

如果你基于一个常量数组定义你的类型:

const PublicMethods = ['Time', 'Assets', 'AssetPairs'] as const;
const PrivateMethods = ['Balance', 'TradeBalance'] as const;

type PublicMethods = typeof PublicMethods[number];
type PrivateMethods = typeof PrivateMethods[number];

你现在可以将名称 PublicMethods 同时用作类型和变量,分别指向字符串和数组的联合类型。有了这个,你可以使用类型谓词创建一个用户定义的类型保护:

function isPublicMethod(value: string): value is PublicMethods {
    // some sort of assertion is needed because
    // it will error that type 'string' is not assignable to type '"Time" | "Assets" | "AssetPairs"'
    return PublicMethods.includes(value as any);
}

这允许您通过检查 PublicMethods(数组)是否包含 value 来将值缩小到 PublicMethods 类型:

function doStuff(method: PrivateMethods | PublicMethods) {
    if (isPublicMethod(method)) {
        method
        // ^? PublicMethods
    }
}

操场