我可以在打字稿中既声明广泛类型又使用推断类型吗?
Can I both declare a broad type and use inferred type in typescript?
我经常发现我想声明一个常量对象是某种宽泛的类型,以便编译器可以检查初始化。但是当我 使用 那个对象时,我想使用特定的 推断 类型。如果我为它声明一个类型,我找不到访问对象的推断类型的方法。示例:
电子表格由指向 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 }
}
有什么方法可以让我的蛋糕也能吃吗?
Is there some way to have my cake and eat it, too?
我能想到的最好的就是这个装置:
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 不允许使用具有两个泛型参数的泛型函数。所以你必须有一个 class 和一个泛型参数,非静态方法和另一个泛型参数(因为静态 class 方法不允许访问泛型 class 参数),这导致冗长的语法 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 }
});
我经常发现我想声明一个常量对象是某种宽泛的类型,以便编译器可以检查初始化。但是当我 使用 那个对象时,我想使用特定的 推断 类型。如果我为它声明一个类型,我找不到访问对象的推断类型的方法。示例:
电子表格由指向 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 }
}
有什么方法可以让我的蛋糕也能吃吗?
Is there some way to have my cake and eat it, too?
我能想到的最好的就是这个装置:
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 不允许使用具有两个泛型参数的泛型函数。所以你必须有一个 class 和一个泛型参数,非静态方法和另一个泛型参数(因为静态 class 方法不允许访问泛型 class 参数),这导致冗长的语法 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 }
});