Введите 'строка | логическое значение не может быть назначено для ввода «никогда» в машинописном тексте

Рассмотрим этот класс:

 const creditRiskRatingKeys = [
  'applicant_id',
  'is_dirty',
] as const;

const dict = {
  applicant_id: 'test_id',
  is_dirty: true,
}

class CreditRiskRating {
  applicant_id: string = '';
  is_dirty: boolean = false;

  fillQuestionDictionaryToModel() {
    for (const key of creditRiskRatingKeys) {
      this[key] = dict[key]; // Type 'string | boolean' is not assignable to type 'never'.
    }
  }
}

Key уже keyof CreditRiskRating, а this относится к одному и тому же классу. Тогда почему this[key] = dict[key]; должны жаловаться?? Что мне здесь не хватает?

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

fillQuestionDictionaryToModel также является ключом CreditRiskRating, которому нельзя назначить строку.
Sjoertjuh 19.06.2024 11:43

«Ключ уже является ключом КредитРейтинга», верно, и это может быть один из applicant_id или fillQuestionDictionaryToModel. Таким образом, значение, которое вы ему присваиваете, должно быть присвоено обоим из них одновременно.

VLAZ 19.06.2024 11:43

@VLAZ Спасибо. Есть ли способ ограничить CreditRiskRatingKeys только свойства, а не методы? Как лучше всего с этим справиться.

Alireza Ahmadi 19.06.2024 11:50

@Sjoertjuh, спасибо. Есть ли у вас какое-либо решение для этого?

Alireza Ahmadi 19.06.2024 11:51

@Sjoertjuh обновил вопрос, и похоже, что это работает не во всех случаях!

Alireza Ahmadi 19.06.2024 12:34

Символ never существует потому, что он является пересечением всех типов свойств (см. ms/TS#30769 ), и поэтому небезопасно назначать что-либо случайно выбранному свойству. Лучший способ справиться с этим, как написано, — изменить его на 'test' as never вместо 'test' as any, но это просто обходной путь. Прямо сейчас возникает вопрос о присвоении anynever, что запрещено. У вас есть некоторый основной вариант использования; не могли бы вы отредактировать, чтобы избежать утверждений типа («приведение») и any, чтобы мы могли увидеть, как выглядит этот вариант использования?

jcalz 19.06.2024 16:00

@jcalz спасибо за комментарий. На самом деле у меня есть словарь вопросов, основанный на некоторых свойствах моего класса. и хочу синхронизировать словарь вопросов и мои свойства в классе. Я обновлю свой вопрос, и мне нужно будет добавить больше зависимостей для моделирования реального варианта использования. Все еще не знаете, почему this[key as keyof this] = 'test' as any; работает?

Alireza Ahmadi 19.06.2024 17:05

потому что keyof this является неявно универсальным, и поэтому TS не будет с готовностью сворачивать его в never. Но это, вероятно, выходит за рамки; вопрос SO должен быть узким по охвату для конкретной вещи (я думаю, основной вариант использования здесь), а не для кучи возможно связанных вещей (все проблемы, которые у вас были с различными попытками... у каждого есть причины, почему работает или нет, но будущие читатели вряд ли пробовали то же самое).

jcalz 19.06.2024 17:09

@jcalz имеет смысл. обновит вопрос, чтобы сосредоточить его на основном варианте использования

Alireza Ahmadi 19.06.2024 17:12

Обновленный вопрос больше не имеет смысла. Вы просите обеспечить типобезопасный способ присвоения строк логическим свойствам. Тогда можно было бы вообще отказаться от использования TypeScript.

Robby Cornelissen 20.06.2024 04:37

@jcalz обновил вопрос. Надеюсь, теперь мой вопрос стал ясен. пожалуйста, дайте мне знать, если нужно больше подробностей

Alireza Ahmadi 20.06.2024 10:53

• Вы все еще пытаетесь распределить any места. Я предлагаю вам прекратить аннотировать вещи и просто позволить TS сделать выводы за вас, чтобы мы могли добраться до стартовой линии. Я бы сказал эта ссылка на игровую площадку показывает минимальный воспроизводимый пример вашей проблемы. Не могли бы вы отредактировать , чтобы использовать текст из этого? • Если да, то я бы сказал, что вы столкнулись с ms/TS#32693 и ms/TS#58905 , и правильный подход показан по этой ссылке на игровую площадку. Это полностью решает вопрос? Если да, то я отвечу; если нет, то чего не хватает?

jcalz 20.06.2024 14:35

@jcalz Да, это именно тот случай, я бы тоже был рад избавиться от «любого». Обновил вопрос. Еще раз спасибо

Alireza Ahmadi 20.06.2024 14:56

Не могли бы вы также не комментировать creditRiskRatingKeys и не использовать as const? Это важно, потому что вы действительно не хотите копировать fillQuestionDictionaryToModel сам по себе, и TS должен об этом знать. Если бы вы просто скопировали текст из здесь в свой вопрос, это было бы полезно... если вы не хотите этого делать, скажите, пожалуйста, почему, чтобы я мог убедиться, что мой ответ не зависит от того, что вы не хочу делать.

jcalz 20.06.2024 14:58

@jcalz Да, для меня это звучит неплохо as const

Alireza Ahmadi 20.06.2024 15:00

Спасибо! Я напишу ответ, когда у меня будет возможность.

jcalz 20.06.2024 15:01
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой 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 для повышения производительности приложения путем загрузки модулей только тогда, когда они...
1
16
187
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

TypeScript не анализирует типы значений свойств, определенных в вашем CreditRiskRatingKeys, чтобы попытаться выяснить, назначаете ли вы правильный тип значения данному свойству.

Одним из решений является введение типа Property, который ограничивает типы значений перечисленных свойств:

export type Property<T, U> = {
  [K in keyof T]: T[K] extends U ? K : never
}[keyof T];

export const CreditRiskRatingKeys: Property<CreditRiskRating, string>[] = [
   'applicant_id'
] as const;

export class CreditRiskRating {
  applicant_id: string = '';

  fillQuestionDictionaryToModel() {
    let f = this[CreditRiskRatingKeys[0]];

    for (const key of CreditRiskRatingKeys) {
      this[key] = 'test';
    }
  }
}

Playground link

Как вы решите этот случай? Детская площадка

Alireza Ahmadi 19.06.2024 12:24

Обновил вопросы, похоже не работает!

Alireza Ahmadi 19.06.2024 12:29

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

Robby Cornelissen 20.06.2024 04:29

Спасибо за ваше внимание. обновил вопрос. надеюсь теперь понятно!

Alireza Ahmadi 20.06.2024 10:54

Тип keyof CreditRiskRating бывает не только applicant_id, но и fillQuestionDictionaryToModel. В JS нет реального различия между методами и свойствами.

Таким образом, хотя CreditRiskRatingKeys является массивом только со значением applicant_id, в зависимости от типа это может быть массив с 'applicant_id' и/или 'fillQuestionDictionaryToModel'. Таким образом, typeof this[key] может быть либо строкой, либо функцией () => void.

Чтобы решить эту проблему, вы можете создать новый тип утилиты PropertyOf, исключающий методы:

type PropertyOf<T> = {
  [K in keyof T]: T[K] extends Function ? never : K
}[keyof T];

и использовать его как

export const CreditRiskRatingKeys: PropertyOf<CreditRiskRating>[] = [
   'applicant_id'
];

export class CreditRiskRating {
  applicant_id: string = '';


  fillQuestionDictionaryToModel() {
    for (const key of CreditRiskRatingKeys) {
      this[key] = 'test';
    }
  }
}

Я нашел ошибку здесь

Alireza Ahmadi 19.06.2024 12:23

Обновил вопросы, похоже не работает!

Alireza Ahmadi 19.06.2024 12:29

Typescript правильно выдает ошибку, поскольку вы присваиваете строковое значение логическому свойству. Тип this[key]never, поскольку для всех свойств класса нет перекрывающегося типа. Что вы пытаетесь достичь?

Arnold Daniels 19.06.2024 12:39

Я не думаю, что это будет проблемой, поскольку я уже использовал это для any. И если это так, то как это работает? this[key as keyof this] = 'test' as any;

Alireza Ahmadi 19.06.2024 12:44

Вы можете использовать (this[key] as any) = 'test'. Обратите внимание, что при этом проверка типа удаляется.

Arnold Daniels 19.06.2024 12:51

В моем реальном сценарии нет кастинга и «любого». я ищу лучшие практики

Alireza Ahmadi 19.06.2024 12:55
Ответ принят как подходящий

По сути, вы столкнулись с microsoft/TypeScript#32693 , а также microsoft/TypeScript#58905. См. эти вопросы для получения канонического ответа.


Назначение формы

bar[k] = foo[k]

где Pick<typeof foo, typeof k> присваивается Pick<typeof bar, typeof k> (используя тип утилиты Pick, чтобы сказать, что нас интересуют только свойства foo и bar, которые существуют в ключах типа k), почти наверняка безопасно. Но TypeScript не видит этого в большинстве случаев.

Первая проблема возникает, когда k имеет тип объединения , например "applicant_id" | "is_dirty". TypeScript отслеживает только тип k, а не его идентичность, поэтому, насколько знает TypeScript, вы пишете

bar[k2] = foo[k1]

где k2 и k1 относятся к одному и тому же типу объединения. Это было бы небезопасно, поэтому TypeScript жалуется. Единственный способ, которым такое присвоение может быть безопасным, — это если foo[k1] имеет тип, который работает для всех возможных k2, который будет пересечением соответствующих типов свойств, а не объединением:

interface Foo {
    a: string,
    b: boolean,
}
interface Bar extends Foo {
    c: Date
}
function f(foo: Foo, bar: Bar, k: keyof Foo) {
    bar[k] = foo[k]; // error!
    //~~~~ <-- Type 'string | boolean' is not assignable to type 'never'.
}

Это тема microsoft/TypeScript#32693, и это давняя открытая проблема в TypeScript.

TypeScript разрешает присвоение bar[k] только в том случае, если это значение одновременно является string и boolean. Такого значения нет... другими словами, string & boolean - это невозможное, никогда не набирайте. По сути, это ошибка, которую вы видите.


Рекомендуемый подход — переключиться с ключей с объединением на универсальные ключи. Таким образом, вместо k типа MyKeys у вас должен быть общий тип K extends MyKeys. И если bar и foo — идентичные типы, то это работает:

function g<K extends keyof Foo>(bar: Foo, foo: Foo, k: K) {
    bar[k] = foo[k]; // okay, because bar is a Foo
}

Но если эти два типа различны, даже если они эквивалентны во всем, что имеет значение, вы снова получите ошибку:

function h<K extends keyof Foo>(bar: Bar, foo: Foo, k: K) {
    bar[k] = foo[k]; // error!
    //~~~~ <-- Property 'c' is missing in type 'Foo' but required in type 'Bar'.
}

Это тема microsoft/TypeScript#58905, и это считается ограничением дизайна TypeScript. Обходной путь — синтезировать собственные типы для bar и foo так, чтобы они воспринимались как идентичные. Это можно сделать, просто расширив их до эквивалентов типов Pick. Поскольку каждый Bar также является Foo, мы можем просто расширить bar и оставить foo в покое:

function i<K extends keyof Foo>(bar: Bar, foo: Foo, k: K) {
    const _bar: Foo = bar; // okay
    _bar[k] = foo[k]; // okay
}

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

type CreditRiskRatingKey = typeof creditRiskRatingKeys[number];

class CreditRiskRating {
    applicant_id: string = '';
    is_dirty: boolean = false;
    fillQuestionDictionaryToModel() {
        const thiz: typeof dict = this;
        creditRiskRatingKeys.forEach(<K extends CreditRiskRatingKey>(key: K) => {
            thiz[key] = dict[key];
        });
    }

}

Здесь я изменил ваш цикл for...of на общий обратный вызов на forEach() и расширил this до typeof dict перед выполнением задания. Теперь компилируется без ошибок. Досадно преодолевать такие препятствия, но это самое близкое к тому, чтобы TypeScript понимал нашу логику. Если вы не хотите этого делать, вы всегда можете использовать утверждения типа например this[key] = dict[key] as never.

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

Большое спасибо за лучшее объяснение. Только одно, можно ссылку microsoft/TypeScript#32693

Alireza Ahmadi 20.06.2024 19:46

Это прямо в верхней части моего ответа.

jcalz 20.06.2024 19:49

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

Почему тип int меняет размер в зависимости от архитектуры процессора, а другие типы — нет?
Определение макроса и использование типов данных для поиска абсолютного значения
Аргумент func с массивом смешанных типов (протокол), затем вызовите статический метод протокола
Не удалось преобразовать число с плавающей запятой 64 в число с плавающей запятой 32 в Python
Как перегрузить процедуру, объявленную в абстрактном типе в Фортране?
Разъяснение: «Значение значения определяется типом выражения, используемого для доступа к нему»
Как изменить неопределенный тип как необязательный в машинописном тексте?
Что вызывает синтаксическую ошибку в агрегате расширения Ada для ограниченного типа? Закомментированный сектин получает синтаксическую ошибку
Как преобразовать тип в универсальный тип, фактический тип которого известен в момент преобразования?
Введите второй аргумент функции динамически на основе типа перечисления первого аргумента