TypeScript не рассматривает доступ к неизвестному свойству как способ сужения потока управления для подтверждения существования этого свойства.Вы можете получить доступ к свойствам типа объекта только в том случае, если компилятор знает, что есть свойство (или может быть, в случае необязательных свойств) свойства для этого ключа.Поэтому, как только вы сделаете PackagingUnits[key]
, где key
- произвольный string
, компилятор пожалуется, что PackagingUnits
не имеет индекса string
.
Самый простой способ исправить это - использовать утверждение типа и двигаться дальше:
function getPackagingUnitName(key: string): PackagingUnits | null {
if ((PackagingUnits as any)[key]) { // assert your way out
return PackagingUnits[key as keyof typeof PackagingUnits]; // assert your way out
}
return null;
}
Это работает, но не гарантируетсябыть типобезопасным компилятором.Возможно, вас это не волнует, поскольку проблема заключается в реализации функции, с которой другие разработчики не собираются связываться.Если это так, то отлично.
Если вы хотите, чтобы компилятор гарантировал, что то, что вы делаете, является безопасным типом, вам нужно провести его через анализ с осторожными шагами.Во-первых, вы бы хотели, чтобы компилятор обрабатывал объект PackagingUnits
не как нечто с тремя известными ключами со значениями PackagingUnits
типов, а вместо этого рассматривал его как нечто, имеющее значение каждого строкового ключа., значение которого PackagingUnits | undefined
.(Это как можно ближе к необязательным индексным сигнатурам , поскольку TypeScript не всегда отличает отсутствующие свойства от undefined
-значений свойств ).Итак, вы хотели бы это:
const PackagingUnitsIndexSignature: {[k: string]: PackagingUnits | undefined} =
PackagingUnits; // error!
Это должно быть безопасно (тип typeof PackagingUnits
является строгим подтипом из typeof PackagingUnitsIndexSignature
), но компилятор не осознает этого.* * * * * * * * * * * * PackagingUnits
природа *1036*, кажется, запутывает это.Таким образом, мы можем сделать двухэтапное расширение ... сначала для простого типа объекта, чьи ключи и значения соответствуют ключам перечисления:
const PackagingUnitsPlainObject: Record<keyof typeof PackagingUnits, PackagingUnits> =
PackagingUnits; // okay!
const PackagingUnitsIndexSignature: { [k: string]: PackagingUnits | undefined } =
PackagingUnitsPlainObject; // okay!
Теперь у нас наконец есть то, что мы можем индексировать с помощью string
без ошибок:
function getPackagingUnitName(key: string): PackagingUnits | null {
if (PackagingUnitsIndexSignature[key]) { // okay!
return PackagingUnitsIndexSignature[key]; // error!!!!
} else {
return null;
}
}
Тьфу, что случилось?Не следует ли контролировать работу по сужению потока, так как мы только что проверили наличие свойства в key
?К сожалению, индексированный доступ типа скобок не всегда сужает значения так, как нам хотелось бы .Вместо того, чтобы заставить это работать, давайте просто примем это как данность, что PackagingUnitsIndexSignature[key]
возвращает PackagingUnits | undefined
.Если это так, мы должны иметь возможность использовать логический оператор "или" (||
) для замены undefined
на null
:
function getPackagingUnitName(key: string): PackagingUnits | null {
return PackagingUnitsIndexSignature[key] || null; // okay
}
И компилятор счастлив,Чтобы собрать все это вместе, вот окольный способ написать ту же логику, чтобы компилятор подтвердил, что она безопасна по типу:
const PackagingUnitsPlainObject: Record<keyof typeof PackagingUnits, PackagingUnits> =
PackagingUnits; // okay!
const PackagingUnitsIndexSignature: { [k: string]: PackagingUnits | undefined } =
PackagingUnitsPlainObject; // okay!
function getPackagingUnitName(key: string): PackagingUnits | null {
return PackagingUnitsIndexSignature[key] || null; // okay!
}
Хорошо, надеюсь, это поможет.Удачи!