【问题标题】:typescript infer raw object type after index signature打字稿在索引签名后推断原始对象类型
【发布时间】:2020-06-07 18:31:00
【问题描述】:

假设我有对象:

const styles = { product: { color: 'blue' } };

由此推断出对象类型,我可以毫无问题地访问foo.bar.baz

但是,如果我想要类型检查,我需要类似的东西:

import type CSS from 'csstype';
type StyleSheet = Record<string,CSS.Properties>;

const styles:StyleSheet = {
    product: { color: 'blue' }
};

上面的内容是有效的,但是,当访问 styles 时,我会得到定义的 styles.whatever.colorstyles.product.margin,这是不希望的。

考虑到这一点,Typescript 中是否有任何方法可以在输入对象后推断其原始类型,以便我可以访问其真实属性,如下所示:

const styles:StyleSheet = { product: { color: 'blue' } }; // typed as 'StyleSheet' so I get type-checking
styles.whatever.margin // TS thinks this is valid because of the index signature;

const original = styles as OriginalObject; // infer original type so only real properties are accessible
original.whatever // invalid (expected)
original.product.color // valid

export default original;

更新

回复@jcalx的:

Playground link

【问题讨论】:

    标签: typescript type-inference


    【解决方案1】:

    您想检查styles 是否为StyleSheet 而没有扩大 stylesStyleSheet 并让编译器忘记它的各个属性。


    做到这一点的一种方法是让编译器推断 styles 的类型,并相信 TypeScript 的结构类型系统会在你在它不喜欢的地方使用它时抱怨。也就是说,假设您有一些需要StyleSheet 的功能:

    function someFunctionThatNeedsAStyleSheet(s: StyleSheet) { }
    

    您可以只制作一些未注释的对象:

    const styles = { product: { color: 'gray' } };
    const badStyles = { product: { colour: 'grey' } };
    

    编译器会记住它们的属性:

    styles.whatever.margin // error;
    styles.product.color // okay
    

    稍后,当您将它们传递给函数时,它会接受它或警告您:

    someFunctionThatNeedsAStyleSheet(styles); // okay
    someFunctionThatNeedsAStyleSheet(badStyles); // error!
    // ----------------------------> ~~~~~~~~~
    // Type '{ colour: string; }' has no properties in common 
    // with type 'Properties<string | 0>' 
    

    在这里进行类型检查。但当然,它不在您声明 styles 变量的同一个地方。


    如果您想在创建时立即捕获错误的StyleSheet,您可以使用上述类型检查功能的想法,通过制作这样的辅助函数:

    const asStyleSheet = <T extends StyleSheet>(t: T) => t;
    

    函数asStyleSheet() 是一个通用的恒等函数:它返回其输入,与输入具有相同的类型。但是泛型类型是constrainedStyleSheet,所以如果你传入一个坏的,你会得到一个错误。像这样:

    const styles = asStyleSheet({ product: { color: 'gray' } }); // okay
    const badStyles = asStyleSheet({ product: { colour: 'grey' } }) // error!
    // ---------------------------------------> ~~~~~~~~~~~~~~
    // 'colour' does not exist in type 'Properties<string | 0>'. 
    // Did you mean to write 'color' ? 
    

    通过使用asStyleSheet(),您正在检查styles 的类型而不更改它; asStyleSheet() 的输出与输入的类型相同。所以编译器会记住它的属性:

    styles.whatever.margin // error;
    styles.product.color // okay
    

    看起来不错。


    好的,希望对您有所帮助;祝你好运!

    Playground link to code

    【讨论】:

    • 非常感谢!那太棒了。但是,只有一件事:在第二个示例中,为什么 const styles = asStyleSheet({ product: { color: 'gray', foo:'bar' } }) 没有显示任何错误,因为 foo 不是 CSS.Properties 的属性,而 const s:StyleSheet = {product:{color: 'gray', foo: 'bar'}}; 按预期无效?
    • 嘿@jcalz,您在此处的回复:stackoverflow.com/a/61811167/165750 指导我制作了这个(针对问题更新了代码的游乐场链接,因为评论太长了)。如果您认为这是实施它的好方法,请告诉我?感谢您对 SO 的回答。
    猜你喜欢
    • 2018-12-15
    • 2018-09-19
    • 1970-01-01
    • 2021-11-12
    • 1970-01-01
    • 2019-10-24
    • 1970-01-01
    • 2021-03-06
    • 1970-01-01
    相关资源
    最近更新 更多