TypeScript存储库中我的问题AsValue
实用程序类型功能请求已被拒绝,2020年11月。我期望以下API:
const MarkupPreprocessingDefaultSettings: AsValue = {
mustExecuteCodeQualityInspection: true,
waitingForOtherFilesWillBeSavedPeriod__milliseconds: 1000,
entryPointsGroupDependent: {
indentationSpacesCountInOutputCode: 2,
mustExecuteHTML5_Validation: true,
mustExecuteAccessibilityInspection: true
}
};
// Same as
const MarkupPreprocessingDefaultSettings: {
mustExecuteCodeQualityInspection: true,
waitingForOtherFilesWillBeSavedPeriod__milliseconds: 1000,
entryPointsGroupDependent: {
indentationSpacesCountInOutputCode: 2,
mustExecuteHTML5_Validation: true,
mustExecuteAccessibilityInspection: true
}
} = {
mustExecuteCodeQualityInspection: true,
waitingForOtherFilesWillBeSavedPeriod__milliseconds: 1000,
entryPointsGroupDependent: {
indentationSpacesCountInOutputCode: 2,
mustExecuteHTML5_Validation: true,
mustExecuteAccessibilityInspection: true
}
};
奥,但我们在现实中有什么?让我们考虑一下具体的问题。
目标
对routing
变量进行注释,例如:
- TypeScript必须知道哪些属性存在,哪些不存在。例如,
routing.products
必须工作,但routing.gibberish
必须强制转换TypeScript错误 - 除了
products
和orders
之外,密钥的数量可以是任意大的 - 不允许任何类型的注释省略、
any
和object
type Route = {
URN: string;
}
const routing /* : ??? */ = {
products: {
URN: "/products"
},
orders: {
URN: "/orders"
}
}
选项1:手动注释所有内容(过度使用(
const routing : {
products: Route;
orders: Route;
// and several thousands of keys for real commercial application ...
} = {
products: {
URN: "/products"
},
orders: {
URN: "/orders"
},
// and several thousands of keys for real commercial application ...
}
结论:非技术性,难以扩展和维护。不可接受的
选项2:使用索引型(松散型安全型(
type Route = {
URN: string;
}
const Routing: { [routeName: string]: Route } = {
products: {
URN: "/products"
},
orders: {
URN: "/orders"
}
}
console.log(Routing.products)
console.log(Routing.orders)
console.log(Routing.gibberish) // Undefined!
结论:它不会防止打字错误;没有可用的自动完成功能。不可接受的
选项3:使用通用辅助函数
const asRouting = <T extends Record<keyof T, Route>>(t: T) => t;
const routing = asRouting({
products: {
URN: "/products"
},
orders: {
URN: "/orders"
}
});
如果您对routing
变量进行了注释,编译器将只将该变量视为已注释类型,并且不会得到任何类型推断。正如你所看到的,要么你必须用正确的类型进行注释,这是多余的,要么或者你注释的类型比你想要的更宽,你就会丢失你关心的类型信息。所以你真的不应该注释routing
。让编译器为您推断它的类型。
但是,如果您错误地使用一个或多个属性不是有效Route
的值初始化routing
,该怎么办?那么,使用routing
的代码将产生编译器错误。但这可能与routing
的定义相去甚远。你希望编译器错误就在那里,这样你就可以修复它。
这就是上面的通用helper函数的作用。asRouting()
在运行时是一个身份函数,只输出与输入相同的值。在编译时,它也是一个标识函数,输出类型将与输入类型相同。但由于这种类型T
被约束为Record<keyof T, Route>
,因此asRouting()
将只接受其所有属性都是有效Route
对象的输入。
让我们看看上面定义的routing
会发生什么。其推断类型可以通过IntelliSense显示为:
/* const routing: {
products: {
URN: string;
};
orders: {
URN: string;
};
} */
这正是你想要的类型,而不需要你拼写出来:
console.log(routing.products.URN); // okay
console.log(routing.orders.URN); // okay
console.log(routing.somethingElse); // compiler error!
// ---------------> ~~~~~~~~~~~~~
// Property 'somethingElse' does not exist
但您仍然需要进行所有类型检查,以确保所有属性都是Route
:
const badRouting = asRouting({
products: {
URL: "/product"; // error!
// ~~~~~~~~~~~~~~~~
// Object literal may only specify known properties,
// and 'URL' does not exist in type 'Route'
}
})
游乐场链接到代码