如何创建允许通过任何字符串查询的类型安全对象

Posted

技术标签:

【中文标题】如何创建允许通过任何字符串查询的类型安全对象【英文标题】:How to create a type-safe object that allows querying by any string 【发布时间】:2021-06-28 11:47:21 【问题描述】:

我想创建一个对象 + getter 函数,如果我们不知道键是否在对象中,它将返回已知值类型(如果键 extends keyof typeof obj)或值类型不是。有点像这样:

const obj = 
  a: 1,
  b: 2,
  c: 3


const a = obj.a
// typeof a = number

const b = obj['b']
// typeof b = number

let key: string = 'anything'
const d = obj[key]
// typeof d = number | undefined
if (d) 
  // typeof d = number
 else 
  // typeof d = undefined

在现实中发生的事情是

let key: string = 'anything'
const d = obj[key]
// TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type ' a: number; b: number; c: number; '.   No index signature with a parameter of type 'string' was found on type ' a: number; b: number; c: number; '.

I've been trying to create a `get` function which would work this way, but can't get it to work. The result type is always either `any` or `unknown`.

【问题讨论】:

我们怎么会不知道key是否存在于一个常量对象中呢? 【参考方案1】:

在我看来,TypeScript 对这种事情有点不满意。但是,我认为通常的做法是编写类型保护。类似于下面的东西。 isObjKey 显式检查实际的 javascript 对象是否包含键 (obj[key] !== undefined) 并在函数返回 true (key is keyof typeof obj) 时告诉 TypeScript 类型。现在,如果我们使用它进行测试,TypeScript 知道当您执行 obj[key] 时它在“if”的第一个分支中有一个数字,我认为这就是您所要求的。不满意之处在于,我们不得不编写一个新的、困难的函数来做一些琐碎的事情,并且我们已经检索了 obj[key] 两次,只是为了在一个常量对象上获得类型安全。

const obj = 
    a: 1,
    b: 2,
    c: 3
;

let key: string = Math.random() < 0.5 ? 'a' : 'anything';

function isObjKey(key: string): key is keyof typeof obj 
    return obj.hasOwnProperty(key);


if (isObjKey(key)) 
    const d = obj[key];
    // d is a number here as far as TypeScript is concerned
    console.log(d);

else 
    // key isn't a valid key of obj, there's no need for another obj[key] access
    // (but if you do it's type any, not undefined)
    console.log('undefined');

另一种方法就是破解它。你向 TypeScript 断言你的 key 肯定是 obj 的 key,即使它可能不是,并且 TypeScript 认为它在 obj[key] 之后有一个数字:

const d = obj[key as keyof typeof obj];
if (d) 
    // TypeScript thinks d is a number
    console.log(d);
 else 
    // TypeScript still thinks d is a number, but it probably doesn't matter!
    console.log('undefined');

【讨论】:

我不确定它是如何令人不满意的,因为在您的示例中,键绝对可能不是 obj 的键。检查它是任何好的代码都应该做的。如果“任何东西”是关键,它应该怎么做?这不是钥匙 谢谢!你的第一个例子我几乎明白了。现在的问题是除了值的实际类型之外,打字稿说它可以是undefined。我知道实际上该对象可能在运行时发生了变化,但是有没有办法告诉 TS 它不能是undefined? (如果用Object.freeze定义,我们知道它不能改变)Playground link 首先,我意识到如果打开 noImplicitAny,我的答案将不起作用,我已经对其进行了编辑以解决该问题。我已将 obj[key] !=== undefined 替换为 obj.hasOwnProperty(key) 我不认为这是你的问题吗?但是,我不明白您的问题是什么:您是说 d 显示为 'number | 'if' 语句中的 undefined'(我在这里写了 'd 是一个数字')?因为那不是我看到的。问题到底出在哪里(以及哪个变量?)?【参考方案2】:

我没听错吗?

const obj: Record<string, any> =  ... ;

const getter = (key: string): string => 
    if (key in obj) return typeof obj[key];
    return undefined;

【讨论】:

这不适用于--strictNullChecks,请参阅here。没有--strictNullChecks 就没有undefined 类型需要担心,但是问题不会要求number | undefined 类型。

以上是关于如何创建允许通过任何字符串查询的类型安全对象的主要内容,如果未能解决你的问题,请参考以下文章

如何使 SignalR 承载身份验证安全或如何不记录查询字符串

ms 访问查询仅检索所需的值

CDI Features

Java 泛型

java编程基础----泛型

我们如何在保持类型安全的同时通用地处理关联类型