Когда массивы можно преобразовать в кортежи с помощью одного утверждения as

Учитывая следующие утверждения, все разрешены:

//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} взаимозаменяемы. Вы говорите «не присваивается свойству только для чтения», но этого никогда не произойдет. • Пожалуйста, отредактируйте вопрос, чтобы уточнить/исправить его.

jcalz 19.07.2024 00:41

Вероятно, я недостаточно ясно сформулировал вопрос, второй пункт должен был быть больше о том, как применять объяснение от первого пункта к пункту на практике.

Max 19.07.2024 20:57

Думаю, я понимаю, что такое только чтение и назначаемость массивов в целом, но я понимаю, что вы имеете в виду. В этом вопросе я просто изолировал переменные, чтобы попытаться понять, но я обновлю, чтобы внести больше ясности.

Max 19.07.2024 21:01

посмотрите as const и const. const означает, что фиксирована ссылка, а не значения в массиве

Daniel A. White 19.07.2024 21:20

Спасибо, думаю, я понял as const против const. Но я не думаю, что это имеет отношение к данному делу. Насколько я могу судить, я никогда не пытаюсь изменить ссылку. В моем примере все ссылки фиксированы. Думаю, я понимаю, что вы имеете в виду для варианта использования as const, хотя обычно я бы использовал это, чтобы получить значения только для чтения, но если я хочу утвердить переменную как некоторый предопределенный тип после операции с картой, я не могу использовать as const, поскольку я не имеют буквальных значений, на основании которых можно было бы утверждать.

Max 19.07.2024 21:27

Назначаемость – это не утверждения. Теперь вы неправильно используете слово «присваивается». Если вы можете написать 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[], но не наоборот. Пожалуйста, отредактируйте, чтобы это исправить.

jcalz 19.07.2024 22:22

спасибо, обновлено, чтобы удалить все упоминания о назначаемости, поскольку меня интересует только достоверность утверждений. И спасибо за объяснение, как связаны назначаемость и утверждения.

Max 20.07.2024 14:26

Учитывая правила назначаемости (⊑) в моем последнем комментарии (readonly [T]readonly T[]; [T]T[]; T[]readonly T[]; [T]readonly [T]), вы можете ожидать успеха 1 и 2, потому что они являются сужениями (X as Y где YX) и 3 добиться успеха, потому что это расширение (X as Y где XY). Но ваш последний пример не является ни расширением, ни сужением и поэтому неверен. Утверждения позволяют двигаться вверх или вниз, а не в сторону. Это полностью решает вопрос? Если да, то я могу написать полный (и, надеюсь, более понятный) ответ. Если нет, то что мне не хватает?

jcalz 20.07.2024 15:01

да, это хорошо объясняет, спасибо

Max 20.07.2024 15:47
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
3
10
79
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Утверждения позволяют расширять или сужать, а не «отклонять в сторону»

Смысл утверждений состоит в том, чтобы позволить вам «повышать» или «понижать» значение до типа. Итак, эмпирическое правило для утверждений таково: если у вас есть значение x типа X, то вы можете написать утверждениеx as Y, если либо X extends Y, либо Y extends X. Если ни одно из этих утверждений не верно, то TypeScript, вероятно, будет жаловаться, что эти два типа недостаточно связаны, чтобы утверждать между собой, и что это, вероятно, ошибка. Вы не можете напрямую «привести» значение к типу.

Обратите внимание, что отношения extendsимеют разные названия, и хотя между ними иногда есть различия, все они представляют собой примерно одно и то же понятие: вы можете сказать «T extends U» или «T присваивается U» или «T является подтипом». U» или «T — это сужение U», или «U — это расширение T», или «TU». Это отношение не симметрично (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

Детская площадка, ссылка на код

спасибо, это очень хорошо объяснено и легко понять. У меня есть вопрос, глядя на предоставленную вами таблицу назначаемости и на то, как она связана с утверждаемостью. Означает ли это, что сужающие утверждения транзитивны, или, говоря это, я делаю какие-то неверные предположения или интерпретации?

Max 20.07.2024 18:27

Утверждения сужения так же транзитивны, как и само сужение. Так что... да, в основном. Бывают странные ситуации, когда X extends Y и Y extends Z, но нет X extends Z. TS не является полностью типобезопасным.

jcalz 20.07.2024 18:45

Другие вопросы по теме