打字稿在索引签名后推断原始对象类型

typescript infer raw object type after index signature

假设我有对象:

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

你想检查 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()是一个通用的身份函数:它returns它的输入,与输入的类型相同。但是泛型是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