[英]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)
推断出ext
是previewTypes
的键,所以在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]
是索引访问类型)。
UPDATE: It turns out it's not too difficult after all to infer that ext
is a key of previewTypes
.更新:事实证明,推断
ext
是previewTypes
的关键毕竟并不难。 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
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.