打字稿接口值作为枚举或记录

时间:2021-04-02 22:21:07

标签: typescript types enums

假设我有一些对象可以将 key 作为字符串,将 value 作为 enum 或键和值作为枚举的对象。

类型

export enum ERRORS {
  ERROR1 = 'Error1',
  ERROR2 = 'Error2',
}

export type ErrorType = 'blue' | 'green';

export interface ErrorByCode {
  [key: string]: ERRORS | Record<ErrorType, ERRORS>;
}

代码

const ERRORS_BY_CODE: ErrorByCode = {
  default: ERRORS.ERROR1,
  '01': ERRORS.ERROR2,

  '2': {
    blue: ERRORS.ERROR1,
    green: ERRORS.ERROR2,
  },
};

const errorResolver = ({code, type}: {code: string; type: ERRORS}) => {
    const errorMessage =
    (ERRORS_BY_CODE?.[code] || ERRORS_BY_CODE?.[code]?.[type]) ??
    ERRORS_COMPONENTS_BY_CODE.default;

    return errorMessage
};


console.log(errorResolver({code: '01', type: 'blue'}))



但是我在 ERRORS_BY_CODE?.[code]?.[type] 下遇到了 ts 错误 元素隐式具有“any”类型,因为类型“ErrorType”的表达式不能用于索引类型“ERRORS |记录<错误类型,错误>'。 类型“ERRORS_BY_CODE”上不存在属性“blue” |记录'。

1 个答案:

答案 0 :(得分:0)

编译器不希望任何人尝试使用 string 之类的键索引 ERRORS(即 "blue"),并且它不允许这样做。它会认为该属性属于 any 类型,并给出编译错误:

function nope(str: string) {
    str.blue // error! Property 'blue' does not exist on type 'string'
}

如果一个值的类型是 stringunion 和其他东西,编译器也会对这样的索引不满意,因为它可能是 string for它只知道:

function stillNope(uni: string | { blue: number }) {
    uni.blue // error! Property 'blue' does not exist on type 'string'
}

Optional chaining 没有解决这个问题。即使您使用 ?. 而不是 .,也不允许您访问对象的未知属性。这项禁令是有意的;有关详细信息,请参阅 microsoft/TypeScript#33736


如果要访问联合类型上的属性,其中至少有一个联合成员不知道该属性具有该属性,则需要使用某种其他形式的类型保护。一种方法是使用 typeof guarding:

function typeofGuard(
  uni: ERRORS | { blue: ERRORS, green: ERRORS }, 
  type: ErrorType
) {
    const errors = typeof uni === "string" ? uni : uni[type];
}

另一种可能性是将您的联合表示为一个联合,其中联合的每个成员在 ErrorType 键上都有已知的行为。您可以将 ERRORS 设置为 string,而不是 ErrorType,后者是在 ERRORS & {[P in ErrorType]?: undefined } 处没有已知行为的 ERRORS。含义:它是 ErrorType,如果您使用 undefined 键对其进行索引,您将获得 function exclusiveUnion( uni: (ERRORS & { [P in ErrorType]?: undefined }) | (Record<ErrorType, ERRORS>), type: ErrorType ) { const errors = uni[type]; // ERRORS | undefined } 属性。这也会使您的错误消失:

$fakeFilesystem = Storage::fake('somediskname');
$proxyMockedFakeFilesystem = Mockery::mock($fakeFilesystem);
$proxyMockedFakeFilesystem->shouldReceive('temporaryUrl')
    ->andReturn('http://some-signed-url.test');
Storage::set('somediskname', $proxyMockedFakeFilesystem);

Playground link to code