Учитывая следующие утверждения, все разрешены:
//1. A readonly number[] can be cast to readonly tuple. Despite the length of array being unknown.
([] as readonly number[]) as readonly [number, number, number]
//2. Similarly, a number[] can be cast to tuple. Again despite the length of the array being unknown.
([] as number[]) as [number, number, number]
//3. Additionally, a number[] can be cast to a readonly number[]
([] as number[]) as readonly number[]
Итак, я понимаю, что можно привести число [] к кортежу. Итак, компилятор принимает, что я хочу сделать это утверждение. Я также могу сделать утверждение, которое преобразует массив в массив, доступный только для чтения.
Поэтому я думаю, что рассматриваю эти два утверждения как отдельные утверждения с независимыми «особенностями» типов. Первый касается только массива и кортежа. И второй с «только для чтения» и «не только для чтения». Поэтому я полагаю, что, пытаясь объединить эти утверждения, я мог бы ожидать, что смогу утверждать массив как кортеж только для чтения, например:
([] as number[]) as readonly [number, number, number]
Но это не допускается. Так что, возможно, мне неправильно думать об этом утверждении как о своего рода композиции/комбинации двух других утверждений, каждое из которых касается только одной особенности типов. Но, скорее, это следует рассматривать как третье независимое утверждение со своими собственными правилами.
Я надеюсь, что это немного более понятно. Вот обновленная ссылка на игровую площадку.
Кроме того, я могу сделать
([] as number[]) as (readonly number[]) as readonly [number, number, number]
Я думаю, это близко к формально более правильному способу сделать это. И если это правда, и тогда вопрос сводится к тому, зачем нужны отдельные утверждения, то я также понимаю, что, возможно, нет лучшего ответа, чем то, как был разработан машинописный текст. Насколько я понимаю, машинописный текст по своему замыслу не определяет какой-то формальный набор правил, которые работают почти математически, а больше ориентирован на простоту использования и удобство в большинстве случаев.
Но в любом случае, если есть идеи, которые помогут более четко понять сценарий, который я обрисовал, это приветствуется. Спасибо.
Похоже, что в вопросе есть потенциально ложные предпосылки: • Знаете ли вы, что T[]
можно присвоить readonly T[]
, а не наоборот? У первого больше известных методов (например, push
), чем у второго. Таким образом, вы никогда не увидите ошибку о том, что изменяемый массив «не может быть назначен» массиву, доступному только для чтения. • Знаете ли вы, что свойства, доступные только для чтения, можно назначать изменяемым и наоборот? {x: string}
и {readonly x: string}
взаимозаменяемы. Вы говорите «не присваивается свойству только для чтения», но этого никогда не произойдет. • Пожалуйста, отредактируйте вопрос, чтобы уточнить/исправить его.
Вероятно, я недостаточно ясно сформулировал вопрос, второй пункт должен был быть больше о том, как применять объяснение от первого пункта к пункту на практике.
Думаю, я понимаю, что такое только чтение и назначаемость массивов в целом, но я понимаю, что вы имеете в виду. В этом вопросе я просто изолировал переменные, чтобы попытаться понять, но я обновлю, чтобы внести больше ясности.
посмотрите as const
и const
. const
означает, что фиксирована ссылка, а не значения в массиве
Спасибо, думаю, я понял as const
против const
. Но я не думаю, что это имеет отношение к данному делу. Насколько я могу судить, я никогда не пытаюсь изменить ссылку. В моем примере все ссылки фиксированы. Думаю, я понимаю, что вы имеете в виду для варианта использования as const
, хотя обычно я бы использовал это, чтобы получить значения только для чтения, но если я хочу утвердить переменную как некоторый предопределенный тип после операции с картой, я не могу использовать as const
, поскольку я не имеют буквальных значений, на основании которых можно было бы утверждать.
Назначаемость – это не утверждения. Теперь вы неправильно используете слово «присваивается». Если вы можете написать declare const x: X; const y: Y = x
, то X
можно присвоить Y
. Если вы можете написать const y = x as Y
, это не значит, что X
можно назначить Y
. Вообще говоря (но это сложнее) утверждение x as Y
допустимо, если X
присваивается Y
или если Y
присваивается X
. [T, T]
можно назначить T[]
, но не наоборот. T[]
можно назначить readonly T[]
, но не наоборот. Пожалуйста, отредактируйте, чтобы это исправить.
спасибо, обновлено, чтобы удалить все упоминания о назначаемости, поскольку меня интересует только достоверность утверждений. И спасибо за объяснение, как связаны назначаемость и утверждения.
Учитывая правила назначаемости (⊑) в моем последнем комментарии (readonly [T]
⊑ readonly T[]
; [T]
⊑ T[]
; T[]
⊑ readonly T[]
; [T]
⊑ readonly [T]
), вы можете ожидать успеха 1 и 2, потому что они являются сужениями (X as Y
где Y
⊑X
) и 3 добиться успеха, потому что это расширение (X as Y
где X
⊑Y
). Но ваш последний пример не является ни расширением, ни сужением и поэтому неверен. Утверждения позволяют двигаться вверх или вниз, а не в сторону. Это полностью решает вопрос? Если да, то я могу написать полный (и, надеюсь, более понятный) ответ. Если нет, то что мне не хватает?
да, это хорошо объясняет, спасибо
Смысл утверждений состоит в том, чтобы позволить вам «повышать» или «понижать» значение до типа. Итак, эмпирическое правило для утверждений таково: если у вас есть значение x
типа X
, то вы можете написать утверждениеx as Y
, если либо X extends Y
, либо Y extends X
. Если ни одно из этих утверждений не верно, то TypeScript, вероятно, будет жаловаться, что эти два типа недостаточно связаны, чтобы утверждать между собой, и что это, вероятно, ошибка. Вы не можете напрямую «привести» значение к типу.
Обратите внимание, что отношения extends
имеют разные названия, и хотя между ними иногда есть различия, все они представляют собой примерно одно и то же понятие: вы можете сказать «T extends U
» или «T
присваивается U
» или «T
является подтипом». U
» или «T
— это сужение U
», или «U
— это расширение T
», или «T
⊑U
». Это отношение не симметрично (T extends U
не подразумевает, что U extends T
), но обычно транзитивно (T extends U
и U extends V
часто подразумевают, что T extends V
).
С другой стороны, отношение утверждаемости симметрично (если вы можете утверждать t as typeof u
, то вы можете утверждать u as typeof t
) и обычно не транзитивно (разрешение t as U as V
не означает, что t as V
разрешено). Таким образом, даже если вы не можете напрямую преобразовать значение t
в тип V
, вы можете найти промежуточный тип U
, который работает, и написать t as U as V
.
Изменяемые типы кортежей , такие как [T]
и [T, T]
, можно назначать неупорядоченным изменяемым типам массивов произвольной длины, таким как T[]
, но не наоборот:
declare let mutArr: number[];
declare let mutTup: [number, number, number];
mutArr = mutTup; // okay
mutTup = mutArr; // error
Такая же связь справедлива между кортежами только для чтения типа readonly [T]
и типами массивов только для чтения такими как readonly T[]
:
declare let roArr: readonly number[];
declare let roTup: readonly [number, number, number];
roArr = roTup; // okay
roTup = roArr; // error
readonly
массив/кортежиИзменяемые типы массивов, такие как T[]
, можно назначить типам массивов readonly
, таким как readonly T[]
, но не наоборот:
roArr = mutArr; // okay
mutArr = roArr; // error
roTup = mutTup; // okay
mutTup = roTup; // error
readonly T[]
T[]
уже
readonly [T, T, T]
[T, T, T]
Итак, если вы хотите сделать выбор между двумя типами, показанными выше, вы можете сделать это, сужая (перемещаясь вправо и/или вниз) или расширяя (перемещаясь вверх и/или влево). Но вы не сможете этого сделать, если делаете какое-то боковое движение (движение вниз и влево или вверх и вправо).
Это означает, что вы можете ожидать, что любое из этих утверждений сработает:
// narrowings
roArr as number[]; // okay
roArr as readonly [number, number, number]; // okay
roArr as [number, number, number]; // okay
mutArr as [number, number, number]; // okay
roTup as [number, number, number]; // okay
// widenings
mutArr as readonly number[]; // okay
roTup as readonly number[]; // okay
mutTup as readonly number[]; // okay
mutTup as number[]; // okay
mutTup as readonly [number, number, number]; // okay
Но вы можете ожидать, что эти утверждения потерпят неудачу:
// sidecasting
mutArr as readonly [number, number, number]; // error
/* Conversion of type 'number[]' to type 'readonly [number, number, number]' may be a
mistake because neither type sufficiently overlaps with the other.
If this was intentional, convert the expression to 'unknown' first. */
roTup as number[]; // error
/* Conversion of type 'readonly [number, number, number]' to type 'number[]' may be a
mistake because neither type sufficiently overlaps with the other.
If this was intentional, convert the expression to 'unknown' first. */
И это действительно то, что происходит. И если вы действительно хотите «побочную трансляцию», вы можете сделать это, используя промежуточный тип, который может быть подтвержден обоими. Компилятор рекомендует unknown
, но вы можете использовать все, что угодно, например другие углы диаграммы:
// widen then narrow
mutArr as readonly number[] as readonly [number, number, number]; // okay
roTup as [number, number, number] as number[]; // okay
// narrow then widen
mutArr as [number, number, number] as readonly [number, number, number]; // okay
roTup as readonly number[] as number[]; // okay
Детская площадка, ссылка на код
спасибо, это очень хорошо объяснено и легко понять. У меня есть вопрос, глядя на предоставленную вами таблицу назначаемости и на то, как она связана с утверждаемостью. Означает ли это, что сужающие утверждения транзитивны, или, говоря это, я делаю какие-то неверные предположения или интерпретации?
Утверждения сужения так же транзитивны, как и само сужение. Так что... да, в основном. Бывают странные ситуации, когда X extends Y
и Y extends Z
, но нет X extends Z
. TS не является полностью типобезопасным.
Было бы лучше задать один основной вопрос , а не два связанных, но разных вопроса. Не могли бы вы отредактировать свой второй вопрос?