#typescript
#typescript
Вопрос:
Часто, когда у меня есть объединение строковых литералов в TypeScript, я хочу создать сопоставление строк с другими значениями. Это включало в себя создание типа, который не позволяет выводить значения, что может раздражать, когда я также хочу получить доступ к типам значений. Например, вот базовая ошибка:
type UserPersona =
| "entrepreneur"
| "programmer"
| "designer"
| "product_manager"
| "marketing_sales"
| "customer_support"
| "operations_hr"
const userPersonaDisplayNames: { [key in UserPersona]: string } = {
entrepreneur: "Entrepreneur",
programmer: "Programmer",
designer: "Designer",
product_manager: "Product Manager",
marketing_sales: "Marketing amp; Sales",
customer_support: "Customer Support",
operations_hr: "Operations amp; HR",
}
Проблема здесь в том, что значения userPersonaDisplayNames
являются типом string
, а не их буквальными значениями.
Когда я оставляю тип, ключи и значения являются литеральными типами, что замечательно, но ключи больше не ограничены типом объединения.
const userPersonaDisplayNames = {
entrepreneur: "Entrepreneur",
programmer: "Programmer",
designer: "Designer",
product_manager: "Product Manager",
marketing_sales: "Marketing amp; Sales",
customer_support: "Customer Support",
operations_hr: "Operations amp; HR",
}
Один из обнаруженных мной способов заключается в использовании extends для assert .
type Assert<A,B> = A extends B
type assertPersonaKeys = Assert<keyof userPersonaDisplayNames, UserPersona>
Это работает, но это довольно грубо. У меня есть неиспользуемый тип, на который жалуется TSLint.
Похоже, что infer
ключевое слово — это именно то, что я ищу, но я не уверен, как это работает, и в моем случае это не работает. В идеале я мог бы сделать что-то вроде этого:
const userPersonaDisplayNames: { [key in UserPersona]: infer } = {
entrepreneur: "Entrepreneur",
programmer: "Programmer",
designer: "Designer",
product_manager: "Product Manager",
marketing_sales: "Marketing amp; Sales",
customer_support: "Customer Support",
operations_hr: "Operations amp; HR",
}
Есть идеи, как решить эту проблему простым способом?
Ответ №1:
Используйте вспомогательную функцию для представления ограничения, которое вы хотите увидеть, а затем вызовите его? Накладные расходы во время выполнения очень малы, поскольку он вызывается (t => t)(obj)
вместо простого использования obj
.
const asUserPersonaDisplayNames = <
S extends string, // allow S to be inferred as a string literal
T extends Record<UserPersona, S> amp; // require keys from UserPersona
Record<Exclude<keyof T, UserPersona>, never> // disallow keys not from UserPersona
>(t: T) => t
const userPersonaDisplayNames = asUserPersonaDisplayNames({
entrepreneur: "Entrepreneur",
programmer: "Programmer",
designer: "Designer",
product_manager: "Product Manager",
marketing_sales: "Marketing amp; Sales",
customer_support: "Customer Support",
operations_hr: "Operations amp; HR",
}); // type has fully string-literal values
const missing = asUserPersonaDisplayNames({
entrepreneur: "Entrepreneur",
programmer: "Programmer",
product_manager: "Product Manager",
marketing_sales: "Marketing amp; Sales",
customer_support: "Customer Support",
operations_hr: "Operations amp; HR",
}) // error, property "designer" is missing
const extra = asUserPersonaDisplayNames({
candlestick_maker: "Rub a Dub Dub",
entrepreneur: "Entrepreneur",
programmer: "Programmer",
designer: "Designer",
product_manager: "Product Manager",
marketing_sales: "Marketing amp; Sales",
customer_support: "Customer Support",
operations_hr: "Operations amp; HR",
}); // error, property "candlestick_maker" is extra
const notString = asUserPersonaDisplayNames({
entrepreneur: 25,
programmer: "Programmer",
designer: "Designer",
product_manager: "Product Manager",
marketing_sales: "Marketing amp; Sales",
customer_support: "Customer Support",
operations_hr: "Operations amp; HR",
}); // error, number is not expected
Надеюсь, это поможет; удачи!
Комментарии:
1. Вы уже написали книгу по TS? Я бы купил это в мгновение ока.