Как намекнуть на машинопись, что строка является последовательной для нескольких операций индексации - PullRequest
1 голос
/ 23 апреля 2020

Минимальный пример проблемы:


const a = {
    'x': {},
    'y': 'y',
};
const b = {
    'x': {},
    'y': 'y',
};
for (const p of ['x', 'y'] as const) {
    a[p] = b[p]; // error string | {} is not assignable to {} & string
}
for (const p of ['x', 'y'] as const) {
    switch (p) {
        case 'x':
            a[p] = b[p]; // no error
            break;
        case 'y':
            a[p] = b[p]; // no error
            break;
    }
}

Typescript видит тип p как 'x' | 'y', но не коррелирует тот факт, что a и b индексируются то же значение.

Есть ли способ ... распространять? операция объединения, так что, по сути, типизированное выражение становится от a['x'|'y'] = b['x'|'y'] до a['x'] = b['x'] | a['y'] = b['y']

Я пробовал обобщенные функции c и множество попыток принудительного приведения типов входных данных, но не утверждал as any кажется, что ничего не работает.

Я бы хотел избежать утверждений типа as (особенно any), так как он добавляет еще одну область, где анализ типов, скорее всего, потерпит неудачу, но я чувствую, что У меня нет других вариантов, кроме генерации кода во время сборки, но это довольно большой дополнительный шаг сборки, я бы не стал добавлять что-то вроде этого маленького.

Оператор switch не выполним альтернатива, так как фактическое количество ключей намного больше 2 и может измениться, так что это потребует добавления множества разделов дублированного кода по всей моей кодовой базе.

1 Ответ

1 голос
/ 23 апреля 2020

Это является следствием улучшения безопасности типов для индексированных типов доступа , введенных в TypeScript 3.5, как реализовано в microsoft / TypeScript # 30769 . В общем случае небезопасно разрешать a[p] = b[q], где a и b относятся к одному и тому же типу объекта, а * p и q относятся к одному и тому же типу объединения, поскольку, если p и q повернуты чтобы быть различными элементами объединения, вы могли бы писать несовместимое значение. В вашем случае вы делаете a[p] = b[p], но с точки зрения системы типов это то же самое; все, что он видит, это то, что вы пишете и читаете свойство объекта, ключ которого имеет тип объединения "x" | "y", что в целом небезопасно. Он не обращает внимания на тот факт, что если вы используете одно и то же значение p в обоих местах, то оно должно быть безопасным.

Так что начиная с TypeScript 3.5 это стало проблемой. Существует запрос на исправление, когда вы читаете или пишете «одно и то же» свойство; см. microsoft / TypeScript # 32693 . И, к счастью, согласно этого комментария похоже, что это будет исправлено для случая, когда вы буквально используете тот же идентификатор (например, p), что и ключ. Не уверен, когда это произойдет, хотя ... проблема, похоже, в бэклоге и не запланирована для конкретного выпуска TypeScript. Так что это может занять некоторое время.


До этого времени должна быть возможность выполнить рефакторинг для универсальной c функции, поскольку в одном месте они по-прежнему допускают более ранний небезопасный доступ до TS-3.5, когда вы используют тип generi c. Это упомянуто в комментарии к # 30769 :

Одно правило, которое мы всегда имели, состоит в том, что любой данный тип (и, соответственно, любой T[K] для идентичных *) 1031 * и K) по определению присваивается самому себе, и это основа c несостоятельности, которую мы допускаем

Так что, если мы введем эту косвенность:

function copyProp<T, K extends keyof T>(dst: T, src: T, key: K) {
    dst[key] = src[key];
}

То компилируется просто отлично, и теперь мы можем использовать его:

for (const p of ['x', 'y'] as const) {
    copyProp(a, b, p);
}

, который также компилируется без ошибок. Это раздражает, но по крайней мере есть решение / обходной путь, который работает на данный момент, по крайней мере, пока не выпущено исправление для # 32693.


Последняя мысль о желании, что это можно исправить в целом, так что вы мог бы избежать переключения операторов. Некоторое время go я открывал запрос функции microsoft / TypeScript # 25051 , чтобы разрешить «анализ потока распределенного управления», где вы могли бы сказать что-то вроде type switch (p) {...}, и чтобы компилятор оценил вложенное кодируйте блок один раз для каждого элемента типа объединения p, и если каждый проход успешен, тогда все будет успешно. Компилятор, по-видимому, не может выполнить такой многопроходный анализ для каждого выражения с типом union, с которым он сталкивается, но я надеялся, что у нас может быть хотя бы какой-то синтаксис для его запроса в определенных c случаях. Увы, это не так (и было закрыто как дубликат одного из нескольких вопросов, которые он решит), но когда я вижу эту проблему, я начинаю задумываться и думаю о том, что могло быть ..., Вздох ...


Хорошо, надеюсь, это поможет; удачи!

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...