简体   繁体   English

如何正确键入 function 以检查 object 中是否存在密钥,并返回相应的值?

[英]How do I correctly type a function that checks whether a key exists in object, and returns the corresponding value?

I held off a long while thinking I would come across the answer, but I still haven't so I'm giving SO a shot我推迟了很长时间,以为我会找到答案,但我仍然没有,所以我试了一下

import { PDFViewer, MSViewer } from './viewerclasses'

//tried adding in just a union of the keys
type ViewerTypes = 'xls' | 'xlsx' | 'doc' | 'docx' | 'pdf';

type PreviewTypes = {
    pdf: typeof PDFViewer;
    doc: typeof PDFViewer;
    docx: typeof PDFViewer;
    xls: typeof MSViewer;
    xlsx: typeof MSViewer;
}


const previewTypes: PreviewTypes = {
    pdf: PDFViewer,
    doc: PDFViewer,
    docx: PDFViewer,
    xls: MSViewer,
    xlsx: MSViewer
};

//attempt #1
type ViewerMap<T> = T extends ViewerTypes ? PreviewTypes[T] : false;
//attempt #2
type ViewerMaybe<T> = T extends keyof PreviewTypes ? PreviewTypes[T] : false

export function getViewer<K extends ViewerTypes>(filename: K): ViewerMaybe<typeof filename> {

    const type = (filename.split('.').pop()?.toLowerCase() as ViewerTypes) || 'unknown';

    const viewer = Object.prototype.hasOwnProperty.call(previewTypes, type) === true
    ? previewTypes[type]
    : false;

    return viewer;
}

But I'm just shooting in the dark here, trying to give getViewer() different types, mapped types, indexed access types, etc, etc, but TS still doesn't really know what I'm trying to do.但是我在这里只是在黑暗中拍摄,试图给 getViewer() 不同的类型、映射类型、索引访问类型等,但是 TS 仍然不知道我在做什么。

I'm trying to type getViewer() correctly, so that Typescript knows if I hand in a key of previewTypes as an argument, I get a constructor back, and if not, I get false back.我正在尝试正确键入 getViewer(),以便 Typescript 知道我是否将 previewTypes 的键作为参数提交,我会返回一个构造函数,如果没有,我会返回 false。 I've brushed over this problem for so long, but I would like to understand the type system enough to tackle it.我已经刷了这么久这个问题,但我想了解类型系统足以解决它。 I know there's a way to create an indexed access type that says something like我知道有一种方法可以创建一个索引访问类型,上面写着类似

type ViewerIndexMap<T> = {
[Prop in keyof T]: Prop in keyof T ? T[Prop] : false 
}

and then,接着,

export function getViewer(filename): ViewerIndexMap<typeof filename>

or something like that或类似的东西

Where am I going wrong?我哪里错了? What am I missing?我错过了什么? I just re-read the TS handbook, and while I feel like mapped types are close, they don't get me exactly where I need to be.我只是重新阅读了 TS 手册,虽然我觉得映射类型很接近,但它们并没有让我准确地到达我需要的位置。

Thank you!谢谢!

Even though learning about mapped types is always a good plan, you don't actually need them here.尽管了解映射类型始终是一个好计划,但您实际上并不需要它们。 By just creating a non-generic function that does a lookup in previewTypes , the correct result type can be inferred.通过创建一个在 previewTypes 中进行查找的非泛型previewTypes ,可以推断出正确的结果类型。

First, you can simplify the types a bit by deriving them from the previewTypes :首先,您可以通过从previewTypes派生来稍微简化类型:

const previewTypes = {
    pdf: PDFViewer,
    doc: PDFViewer,
    docx: PDFViewer,
    xls: MSViewer,
    xlsx: MSViewer
} 

type PreviewTypes = typeof previewTypes
// evaluates to { pdf: typeof PDFViewer, .., xlsx: typeof MSViewer }
type ViewerTypes = keyof PreviewTypes
// evaluates to "pdf" | "doc" | "docx" | "xls" | "xlsx"

For the lookup function I would use something like对于查找 function 我会使用类似

export function getViewer(filename: string) {
  const ext = filename.split('.').pop()?.toLowerCase()

  return ext !== undefined && previewTypes.hasOwnProperty(ext)
         ? previewTypes[ext as ViewerTypes]
         : false
}

Because TypeScript cannot infer that ext is a key of previewTypes from the hasOwnProperty(ext) in the condition, there's a cast ext as ViewerTypes at the previewTypes index.因为 TypeScript 无法从条件中的hasOwnProperty(ext)推断出extpreviewTypes的键,所以在previewTypes索引处有一个ext as ViewerTypes It's best to avoid as whenever possible, but in cases where the code is obviously correct, and it's a big nuisance to do it type safe, it can be acceptable.最好as避免,但在代码明显正确的情况下,并且确保类型安全是一个很大的麻烦,它是可以接受的。

The inferred return type for getViewer is false | typeof PDFViewer | typeof MSViewer getViewer的推断返回类型为false | typeof PDFViewer | typeof MSViewer false | typeof PDFViewer | typeof MSViewer false | typeof PDFViewer | typeof MSViewer but you could also explicitly specify the signature false | PreviewTypes[ViewerTypes] false | typeof PDFViewer | typeof MSViewer但您也可以显式指定签名false | PreviewTypes[ViewerTypes] false | PreviewTypes[ViewerTypes] (where PreviewTypes[ViewerTypes] is an indexed access type ). false | PreviewTypes[ViewerTypes] (其中PreviewTypes[ViewerTypes]索引访问类型)。

Playground Link 游乐场链接

UPDATE: It turns out it's not too difficult after all to infer that ext is a key of previewTypes .更新:事实证明,推断extpreviewTypes的关键毕竟并不难。 With the following hasOwnProperty function (see this TypeScript issue )使用以下hasOwnProperty function(请参阅此 TypeScript 问题

function hasOwnProperty<T extends object>(o: T, v: PropertyKey): v is keyof T {
  return o.hasOwnProperty(v)
}

you can rewrite the return statement as您可以将 return 语句重写为

  return ext !== undefined && hasOwnProperty(previewTypes, ext)
         ? previewTypes[ext] : false

Playground Link 游乐场链接

It is probably worth noting that although clearer, the code is still not type safe.可能值得注意的是,虽然更清晰,但代码仍然不是类型安全的。 By using is you tell TypeScript to trust that the result of hasOwnProperty correctly establishes whether the argument is a valid key.通过使用is你告诉 TypeScript 相信hasOwnProperty的结果正确地确定了参数是否是有效的密钥。 If you were to use the incorrect return.o.hasOwnProperty(v) for example, this would not yield a type error.例如,如果您使用不正确的return.o.hasOwnProperty(v) ,则不会产生类型错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM