Это не отменяет вашу явную типизацию ... подчиняется вашей явной типизации, обрабатывая list
как массив элементов типа X
.X
может быть A
, B
или C
.Если я передам вам значение типа X
, можно прочитать свойство one
, поскольку оно определенно существует.Но небезопасно пытаться прочитать свойство two
, потому что X
может быть A
, а A
, как известно, не имеет two
.Таким образом, вы получаете полезную ошибку:
z.two; // error!
// Property 'two' does not exist on type 'X'. Property 'two' does not exist on type 'A'.
Итак, каковы ваши варианты?Одним из них является просто сообщить компилятору, что вы знаете лучше, чем он, используя утверждение типа , как в другом ответе:
(z as B).two; // okay now
Это подавляет ошибку компилятора, но это действительноне очень хорошее решение, потому что оно частично отключает проверку типов, когда в этом нет необходимости.Следующее также не будет ошибкой, но это вызовет проблемы во время выполнения:
(list[0] as B).two.length; // no compile error
// at runtime: TypeError: list[0].two is undefined
Обычно утверждения типа должны быть последним средством решения ошибок компилятора, которые должны использоваться только там, где вы не можете найтиразумный способ убедить компилятор в том, что то, что вы делаете, безопасно, и вы уверены, что это безопасно и останется безопасным даже перед лицом возможных изменений кода (например, вы измените list
на [b,c,a]
в будущем).
Лучшее решение - использовать type guard , чтобы убедить компилятор в том, что то, что вы делаете, безопасно.Это оказывает влияние на время выполнения, поскольку вы выполняете больше кода, но код, который вы выполняете, более перспективен на будущее, если вы измените list
где-нибудь в будущем.Вот способ сделать это:
const z = list[2]; // z is inferred as A | B | C
if ('two' in z) {
// z is now narrowed to B | C
z.two; // okay
}
Таким образом, вы защищаете чтение z.two
, используя in
, чтобы проверить наличие свойства two
перед его использованием.Теперь это безопасно.Если вы думаете, «почему я должен проходить через эту проблему, когда я знаю, что z
будет иметь тип B
(на самом деле C
, ха-ха, list[2]
является третьим элементом)», затем читайте:
Если вы уверены, что list
всегда будет трехэлементным массивом типов A
, B
и C
, в этом порядке, тоВы можете сообщить об этом компилятору и получить ожидаемое поведение во время компиляции без какой-либо защиты во время выполнения.Вы ищете типов кортежей :
const list: [A, B, C] = [a, b, c];
const z = list[2];
z.two; // okay
z.three; // okay
Вы сказали компилятору, что list
- это трехэлементный кортеж типа [A, B, C]
.Теперь ошибок нет (и видно, что z
- это C
).Это безопасно с нулевым воздействием во время выполнения.Он также не позволяет вам связываться с list
:
list[1] = a; // error! A is not assignable to B
, поскольку вы сказали, что list[1]
всегда является B
, тогда вы должны назначить ему что-то совместимое с B
:
list[1] = c; // okay, C is assignable to B
Итак, у вас есть три варианта: утверждения, защита типов и кортежи, и я рекомендую для этой ситуации кортежи.Надеюсь, это поможет.Удачи!