【问题标题】:Ignore "unique symbol" in typescript when using "keyof typeof" with Object.keys()将“keyof typeof”与 Object.keys() 一起使用时忽略打字稿中的“唯一符号”
【发布时间】:2021-06-22 16:39:38
【问题描述】:

我正在尝试显示当前 redux 状态下的所有值,但遇到涉及 unique symbol 类型的错误,我不知道如何解决。

Object.keys() 说它正在返回 string[]keyof typeof state 包含 unique symbol 所以 .map() 不起作用。我添加了一个 .filter() 子句,但这没有帮助。

这在 javascript 中运行时可以正常工作,但 typescript 会尖叫并拒绝编译。

简化示例:

const state = useAppSelector(state => state);
return (
    <div>
        {Object.keys(state).filter((x: keyof typeof state) => x instanceof String).map((x) =>
            <div>
                <label>{x}</label>
                <div>
                    {Object.keys(state[x]).filter((y: keyof typeof state[x]) => y instanceof String).map((y) =>
                        <span>{y}: {JSON.stringify(state[x][y])}</span>
                    )}
                </div>
            </div>
        )}
    </div>
);

如何正确输入?

编辑:

我尝试使用Omit&lt;&gt; 实用程序类型:

Object.keys(state).map((x: Omit&lt;keyof typeof state, symbol&gt;) =&gt;

但这仍然会导致通过state[x]访问状态时出错:

Type 'Omit&lt;"auth" | "developer" | "errors" | "info" | unique symbol, symbol&gt;' cannot be used as an index type. ts(2538)

【问题讨论】:

    标签: typescript react-redux


    【解决方案1】:

    Object.keys 仅根据规范返回字符串,因此您的 .filter() 调用没有任何帮助。 Object.keys() 不返回 keyof T[] 的原因在 this answer 中有相当详尽的说明。


    阅读了链接的答案,并了解在这些类型的情况下使用keyof 是不安全的,有两种使用Object.entries 的方法。

    第一个安全版本是将值类型转换为unknown,并在需要实际类型时使用类型保护:

    // Object.entries throws on null/undefined
    function isEntriesCallable(arg: T): arg is Record<any,unknown> {
        return arg !== null && typeof arg !== "undefined";
    }
    
    const state = useAppSelector(state => state);
    return (
        <div>
            {Object.entries(state as Record<any, unknown>).map(([x, stateX]) =>
                <div>
                    <label>{x}</label>
                    <div>
                        { isEntriesCallable(stateX) ? Object.entries(stateX).map(([y, stateY]) =>
                            <span>{y}: {JSON.stringify(stateY)}</span>
                        ) : <span>null/undefined</span>}
                    </div>
                </div>
            )}
        </div>
    );
    

    优点/缺点是现在所有的值类型都是unknown,如果你想缩小它们,你必须调用类型保护。在您的示例中,JSON.stringify 接受 any,因此具有 unknown 值实际上不会影响任何事情。如果开发人员想要调用除 anyunknown 之外的任何需要类型的东西,那么未来对代码的任何编辑都将迫使开发人员使用类型保护。


    一个不太安全的版本会受到链接答案中突出显示的过多属性问题的影响。但是,如果您知道类型是准确的,这会将stateX 缩小为typeof state 的字符串键属性值类型的并集。它使用对映射类型的强制转换,该类型执行您对Omit 的预期。提供的类型保护还允许对 stateX 值类型进行某些类型缩小,以排除 stateX 上的原始值或 null 值,这样您就不必费心在原始值/null 上调用 Object.entries

    // Extract an object containing only string property keys
    // guarantees your type will match `{[key: string]: T}` for the
    // argument to the Object.entries type definition.
    type StringKeyedObject<T extends object, K extends keyof T = keyof T> = {
        [Key in (K extends string ? K : never)]:  T[Key];
    }
    
    type NonObject = number | string | boolean | symbol | bigint | undefined | null;
    function isObjectLike<T extends object>(arg: T | NonObject): arg is T { return arg !== null && typeof arg === "object"; }
    
    const state = useAppSelector(state => state);
    return (
        <div>
            {Object.entries(state as StringKeyedObject<typeof state>).map(([x, stateX]) =>
                <div>
                    <label>{x}</label>
                    <div>
                        {isObjectLike(stateX) ? Object.entries(stateX as StringKeyedObject<typeof stateX>).map(([y, stateY]) =>
                            <span>{y}: {JSON.stringify(stateY)}</span>
                        ) : <span>{JSON.stringify(stateX)}</span>}
                    </div>
                </div>
            )}
        </div>
    );
    

    优点:

    • 使用您自己声明的类型进行推理/缩小。

    缺点:

    • 不安全的类型转换可能会导致类型定义中不存在的未处理对象属性错误。

    【讨论】:

    • 非常感谢!这是一个令人沮丧的问题。我使用了第二种解决方案,因为事情的类型更好,而且我的 redux 商店没有使用部分类型。因为我使用的是 eslint,所以我确实必须添加一条 "@typescript-eslint/ban-types": "off" 规则,这样它就不会抛出关于 object 的错误,我认为这应该是一个警告。
    猜你喜欢
    • 2021-08-14
    • 1970-01-01
    • 2017-07-31
    • 2022-01-09
    • 2017-08-15
    • 2017-12-25
    • 2021-02-13
    • 2019-10-06
    • 2019-04-18
    相关资源
    最近更新 更多