Учитывая следующие утверждения, все разрешены:
//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 не является полностью типобезопасным.
Было бы лучше задать один основной вопрос , а не два связанных, но разных вопроса. Не могли бы вы отредактировать свой второй вопрос?