我可以声明一个宽泛的类型并在打字稿中使用推断类型吗?

时间:2017-05-10 20:29:28

标签: typescript type-inference inferred-type

我经常发现我想声明一个常量对象是某种宽泛的类型,因此编译器可以检查初始化。但是当我使用该对象时,我想使用特定的推断的类型。如果我为它声明一个类型,我找不到访问对象的推断类型的方法。例如:

电子表格由指向CSS属性集合的字符串组成。初始化电子表格时,我想强制每个样式的成员都是CSS属性。所以:

type MyStyleDeclaration<K extends string = string> = { [key in K]: CSSProperties }

const myStyleSheet:MyStyleDeclaration {
    aStyle: { margin: 4 }
}

这强制我的CSS属性margin存在,但如果我后来愚蠢地尝试访问

myStyleSheet.notAStyle

编译器不知道任何错误 - 密钥可以是任何字符串。

另一方面,如果我没有声明myStyleSheet的类型,编译器将正确检测到myStyleSheet.notAStyle之类的错误引用。如果我将myStyleSheet传递给声明为MyStyleDeclaration<K>的泛型函数,K将被正确推断为只是对象中的键。

但是,当然编译器现在没有发现任何错误:

const myStyleSheet {
    aStyle: { notAProperty: 4 }
}

有没有办法让我的蛋糕吃掉呢?

1 个答案:

答案 0 :(得分:1)

  

有没有办法让我的蛋糕吃掉呢?

我能想到的最好的就是这个装置:

class Checker<DeclaredType> {
    check<InferredType extends DeclaredType>(t: InferredType): InferredType {
        return t;
    }
}

你可以像这样使用它:

type CSSProperties = { margin: number };

type MyStyleDeclaration<K extends string = string> = {[key in K]: CSSProperties }

const myStyleSheet1 = new Checker<MyStyleDeclaration>().check({
    aStyle: { notAProperty: 4 }
});

// Argument of type '{ aStyle: { notAProperty: number; }; }' is not assignable 
// to parameter of type 'MyStyleDeclaration<string>'.
//   Property 'aStyle' is incompatible with index signature.
//     Type '{ notAProperty: number; }' is not assignable to type 'CSSProperties'.
//       Object literal may only specify known properties, and 'notAProperty' 
//       does not exist in type 'CSSProperties'.

    let p1 = myStyleSheet1.notAStyle;
// no error, but myStyleSheet1 has already failed type checking, so anyway...

const myStyleSheet2 = new Checker<MyStyleDeclaration>().check({
    aStyle: { margin: 4 }
});
// ok

let p2 = myStyleSheet2.notAStyle;
// Property 'notAStyle' does not exist on type '{ aStyle: { margin: number; }; }'.

我根本不喜欢这样。

首先,它为每次检查添加了未使用的对象创建和方法调用,但我不认为可以避免运行时开销。毕竟,您希望检查内置于该语言中。

其次,它的详细信息 - 您只能通过一个函数调用来进行自定义检查。不幸的是,当从实际参数推断出一个参数而另一个参数是显式的时,Typescript不允许使用具有两个通用参数的泛型函数。所以你必须有一个带有一个泛型参数的类,而非静态方法带有另一个参数(因为不允许静态类方法访问泛型类参数),这导致了详细的语法new Checker<SomeType>().check({...})。看起来太像Java了。

<小时/> 更新

事实上,正如Ed Staub所说,它可以简化:

type CSSProperties = { margin: number };

type MyStyleDeclaration<K extends string = string> = {[key in K]: CSSProperties }

function checker<DeclaredType>() {
  return function<InferredType extends DeclaredType>(t: InferredType):InferredType{
      return t;
  }
}

const styleChecker = checker<MyStyleDeclaration>();

const myStyleSheet1 = styleChecker({
    aStyle: { notAProperty: 4 }
});