TypeScript Тип 'строка' не может быть назначен типу

Вот что у меня есть в fruit.ts

export type Fruit = "Orange" | "Apple" | "Banana"

Сейчас я импортирую fruit.ts в другой машинописный файл. Вот что у меня есть

myString:string = "Banana";

myFruit:Fruit = myString;

Когда я делаю

myFruit = myString;

Я получаю сообщение об ошибке:

Тип «строка» не может быть присвоен типу «Оранжевый» | «Яблоко» | "Банан"'

Как я могу присвоить строку переменной произвольного типа Fruit?


person user6123723    schedule 22.06.2016    source источник
comment
Вы совершенно уверены в использовании одинарных и двойных кавычек в export type Fruit?   -  person Weather Vane    schedule 23.06.2016
comment
@WeatherVane Только что проверил мой Fruit.ts. У меня есть одинарные кавычки для типа экспорта Fruit = 'Orange' || «Яблоко || 'Банан. Спасибо   -  person user6123723    schedule 23.06.2016
comment
Мне все еще кажутся двойные кавычки ...   -  person Weather Vane    schedule 23.06.2016


Ответы (11)


Вам потребуется преобразовать его:

export type Fruit = "Orange" | "Apple" | "Banana";
let myString: string = "Banana";

let myFruit: Fruit = myString as Fruit;

Также обратите внимание, что при использовании строковых литералов вам понадобятся использовать только один |

Редактировать

Как упоминалось в другом ответе @Simon_Weaver, теперь можно утверждать его на const:

let fruit = "Banana" as const;
person Nitzan Tomer    schedule 22.06.2016
comment
хороший :) в большинстве случаев подойдет const myFruit: Fruit = "Banana". - person Jacka; 04.08.2017
comment
Могу я сделать обратное? Я имею в виду что-то вроде этого let myFruit:Fruit = "Apple" let something:string = myFruit as string Это дает мне ошибку: преобразование типа «Fruit» в тип «string» может быть ошибкой. - person Siraj Alam; 17.12.2018
comment
@Siraj Это должно работать нормально, вам даже не нужна as string часть. Я пробовал ваш код на детской площадке, и ошибок нет. - person Nitzan Tomer; 17.12.2018
comment
@NitzanTomer stackoverflow.com/questions/53813188/ Пожалуйста, прочтите мой подробный вопрос - person Siraj Alam; 17.12.2018
comment
Но теперь, если я опечатаю const myString: string = 'Bananaaa';, я не получаю ошибок компиляции из-за приведения ... разве нет возможности сделать это при проверке типа строки? - person blub; 25.09.2019
comment
@usagidon Вы пробовали вариант as const, описанный в другом ответе? - person Nitzan Tomer; 25.09.2019
comment
Спасибо, Ницан, действительно const myFruit: Fruit = 'Bananaaa' as const; выдает ошибки компиляции, а const myFruit: Fruit = 'Bananaaa' as Fruit; - нет. Ответ Simon_Weaver должен быть новым принятым ответом, не могли бы вы отредактировать свой ответ, чтобы включить новое утверждение const? - person blub; 09.10.2019
comment
О боже. Мне потребовалось много времени, чтобы найти краткий способ приведения типов, способ Angular требует создания встроенной стрелочной функции. Спасибо. - person Robin Card; 22.11.2019

Типскрипт 3.4 представил новое утверждение 'const'

Теперь вы можете предотвратить «расширение» литеральных типов (например, 'orange' или 'red') до типа string с помощью так называемого утверждения const.

Вы сможете:

let fruit = 'orange' as const;  // or...
let fruit = <const> 'orange';

И тогда он больше не превратится в string - что является корнем проблемы в вопросе.

Дополнительный совет: вы также можете использовать <const> false или <const> true для представления логического значения, которое должно быть истинным или ложным. Иногда это может быть полезно в дискриминируемых союзах. Вы узнаете это, когда увидите.

person Simon_Weaver    schedule 27.03.2019
comment
Для людей, у которых, как и я, еще нет версии 3.4. ‹Const› можно заменить любым, но он, конечно, не такой чистый, как это решение. - person Pieter De Bie; 15.05.2019
comment
Предпочтительный синтаксис будет let fruit = 'orange' as const; при соблюдении правила утверждения типа без угловых скобок - person ThunderDev; 12.06.2019
comment
Это правильный ответ для современного машинописного текста. Это предотвращает ненужный импорт типов и позволяет структурной типизации работать правильно. - person James McMahon; 19.09.2019
comment
Люблю это решение, спасибо, Саймон. - person Kris Boyd; 16.06.2021

Когда вы это сделаете:

export type Fruit = "Orange" | "Apple" | "Banana"

... вы создаете тип с именем Fruit, который может содержать только литералы "Orange", "Apple" и "Banana". Этот тип расширяет String, поэтому его можно присвоить String. Однако String НЕ расширяет "Orange" | "Apple" | "Banana", поэтому его нельзя присвоить ему. String является менее конкретным. Это может быть любая строка.

Когда вы это сделаете:

export type Fruit = "Orange" | "Apple" | "Banana"

const myString = "Banana";

const myFruit: Fruit = myString;

...оно работает. Почему? Поскольку фактический тип myString в этом примере - "Banana". Да, "Banana" - это тип. Он расширяет String, поэтому его можно назначить String. Кроме того, тип расширяет тип объединения, когда он расширяет любые его компоненты. В этом случае "Banana", тип, расширяет "Orange" | "Apple" | "Banana", потому что он расширяет один из своих компонентов. Следовательно, "Banana" можно присвоить "Orange" | "Apple" | "Banana" или Fruit.

person André Pena    schedule 29.06.2018
comment
Забавно, что вы можете поместить <'Banana'> 'Banana', и это приведет к преобразованию строки "Banana" в "Banana" тип !!! - person Simon_Weaver; 15.12.2018
comment
Но теперь вы можете сделать <const> 'Banana', что лучше :-) - person Simon_Weaver; 18.07.2019

Есть несколько ситуаций, в которых возникает именно эта ошибка. В случае OP было значение , определенное явно как строка. Поэтому я должен предположить, что, возможно, это произошло из раскрывающегося списка, или веб-службы, или необработанной строки JSON.

В этом случае простое приведение <Fruit> fruitString или fruitString as Fruit является единственным решением (см. Другие ответы). Вы никогда не сможете улучшить это во время компиляции. [Изменить: см. мой другой ответ о <const>]!

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


Прежде всего: Почему «магические» строковые константы часто лучше, чем перечисления?

  • Мне нравится, как строковая константа выглядит по сравнению с перечислением - она ​​компактна и 'javascripty'
  • Имеет больше смысла, если компонент, который вы используете, уже использует строковые константы.
  • Необходимость импортировать "тип перечисления" только для получения значения перечисления может быть проблемной сама по себе.
  • Что бы я ни делал, я хочу, чтобы это было безопасно для компиляции, поэтому, если я добавлю, удаляю допустимое значение из типа объединения или ошибаюсь, он ДОЛЖЕН выдать ошибку компиляции.

К счастью, когда вы определяете:

export type FieldErrorType = 'none' | 'missing' | 'invalid'

... вы фактически определяете объединение типов, где 'missing' на самом деле является типом!

Я часто сталкиваюсь с ошибкой «не назначается», если у меня есть строка типа 'banana' в моем машинописном тексте, а компилятор думает, что я имел в виду ее как строку, тогда как мне действительно хотелось, чтобы она была типа banana. Насколько умным будет компилятор, будет зависеть от структуры вашего кода.

Вот пример того, когда я получил эту ошибку сегодня:

// this gives me the error 'string is not assignable to type FieldErrorType'
fieldErrors: [ { fieldName: 'number', error: 'invalid' } ]

Как только я узнал, что 'invalid' или 'banana' могут быть типом или строкой, я понял, что могу просто утвердить строку в этот тип. По сути, приведите его к самому себе и скажите компилятору нет, я не хочу, чтобы это была строка!

// so this gives no error, and I don't need to import the union type too
fieldErrors: [ { fieldName: 'number', error: <'invalid'> 'invalid' } ]

Так что же плохого в том, чтобы просто "преобразовать" в FieldErrorType (или Fruit)

// why not do this?
fieldErrors: [ { fieldName: 'number', error: <FieldErrorType> 'invalid' } ]

Это небезопасно во время компиляции:

 <FieldErrorType> 'invalidddd';  // COMPILER ALLOWS THIS - NOT GOOD!
 <FieldErrorType> 'dog';         // COMPILER ALLOWS THIS - NOT GOOD!
 'dog' as FieldErrorType;        // COMPILER ALLOWS THIS - NOT GOOD!

Почему? Это машинописный текст, поэтому <FieldErrorType> является утверждением, а вы сообщаете компилятору, что собака имеет тип FieldErrorType! И компилятор это позволит!

НО, если вы сделаете следующее, компилятор преобразует строку в тип

 <'invalid'> 'invalid';     // THIS IS OK  - GOOD
 <'banana'> 'banana';       // THIS IS OK  - GOOD
 <'invalid'> 'invalidddd';  // ERROR       - GOOD
 <'dog'> 'dog';             // ERROR       - GOOD

Остерегайтесь таких глупых опечаток:

 <'banana'> 'banan';    // PROBABLY WILL BECOME RUNTIME ERROR - YOUR OWN FAULT!

Другой способ решения проблемы - приведение родительского объекта:

Мои определения были следующими:

тип экспорта FieldName = 'number' | 'expirationDate' | 'cvv'; тип экспорта FieldError = 'none' | «пропал без вести» | 'неверный'; тип экспорта FieldErrorType = {field: FieldName, error: FieldError};

Допустим, мы получаем ошибку с этим (строка не назначаемая ошибка):

  fieldErrors: [ { field: 'number', error: 'invalid' } ]

Мы можем «утвердить» весь объект как FieldErrorType следующим образом:

  fieldErrors: [ <FieldErrorType> { field: 'number', error: 'invalid' } ]

Тогда нам не придется делать <'invalid'> 'invalid'.

А как насчет опечаток? Не <FieldErrorType> просто утверждает то, что находится справа от этого типа. Не в этом случае - к счастью, компилятор БУДЕТ жаловаться, если вы это сделаете, потому что он достаточно умен, чтобы знать, что это невозможно:

  fieldErrors: [ <FieldErrorType> { field: 'number', error: 'dog' } ]
person Simon_Weaver    schedule 19.01.2019
comment
Со строгим режимом могут быть свои тонкости. Обновит ответ после подтверждения. - person Simon_Weaver; 25.01.2019

Я вижу, что это немного устарело, но здесь может быть лучшее решение.

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

Например:

enum Fruit {
    Orange = "Orange",
    Apple  = "Apple",
    Banana = "Banana"
}

let myFruit: Fruit = Fruit.Banana;

Теперь вы знаете, что, несмотря ни на что, myFruit всегда будет строкой «Banana» (или любым другим перечисляемым значением, которое вы выберете). Это полезно для многих вещей, будь то группировка подобных значений, подобная этой, или сопоставление удобных для пользователя значений с понятными для машины значениями, все при обеспечении и ограничении значений, допускаемых компилятором.

person Steve Adams    schedule 27.04.2018
comment
Это забавно, потому что у меня возникает эта проблема, когда я пытаюсь уйти от этого. Это все больше сводит меня с ума. Я пытаюсь быть «javascripty» и использовать магические строки, привязанные к типу (вместо перечисления), и, похоже, это все больше и больше дает обратный эффект и колеблется во всем моем приложении с этой ошибкой: - / - person Simon_Weaver; 15.12.2018
comment
Это также вызывает обратную проблему. Вы больше не можете делать let myFruit: Fruit = "Banana". - person Sean Burton; 28.01.2020

Все приведенные выше ответы действительны, однако в некоторых случаях строковый литерал является частью другого сложного типа. Рассмотрим следующий пример:

  // in foo.ts
  export type ToolbarTheme = {
    size: 'large' | 'small',
  };

  // in bar.ts
  import { ToolbarTheme } from './foo.ts';
  function useToolbarTheme(theme: ToolbarTheme) {/* ... */}

  // Here you will get the following error: 
  // Type 'string' is not assignable to type '"small" | "large"'.ts(2322)
  ['large', 'small'].forEach(size => (
    useToolbarTheme({ size })
  ));

У вас есть несколько решений, чтобы исправить это. Каждое решение действительно и имеет свои варианты использования.

1) Первое решение - определить тип для размера и экспортировать его из foo.ts. Это хорошо, если нужно работать с параметром размера самостоятельно. Например, у вас есть функция, которая принимает или возвращает параметр размера типа, и вы хотите его ввести.

  // in foo.ts
  export type ToolbarThemeSize = 'large' | 'small';
  export type ToolbarTheme = {
    size: ToolbarThemeSize
  };

  // in bar.ts
  import { ToolbarTheme, ToolbarThemeSize } from './foo.ts';
  function useToolbarTheme(theme: ToolbarTheme) {/* ... */}
  function getToolbarSize(): ToolbarThemeSize  {/* ... */}

  ['large', 'small'].forEach(size => (
    useToolbarTheme({ size: size as ToolbarThemeSize })
  ));

2) Второй вариант - просто привести его к типу ToolbarTheme. В этом случае вам не нужно открывать внутреннюю часть ToolbarTheme, если она вам не нужна.

  // in foo.ts
  export type ToolbarTheme = {
    size: 'large' | 'small'
  };

  // in bar.ts
  import { ToolbarTheme } from './foo.ts';
  function useToolbarTheme(theme: ToolbarTheme) {/* ... */}

  ['large', 'small'].forEach(size => (
    useToolbarTheme({ size } as ToolbarTheme)
  ));
person Saman    schedule 12.03.2020

У меня была аналогичная проблема при передаче реквизита в компонент React.

Причина: My type inference on myArray wasn't working correctly

https://codesandbox.io/s/type-string-issue-fixed-z9jth?file=/src/App.tsx

Особая благодарность Брэди из Reactiflux за помощь в решении этой проблемы.

person Alfrex92    schedule 04.08.2020

В массивах с растяжением эту ошибку все же можно ввести в заблуждение:

export type Fruit = "Orange" | "Apple" | "Banana"
export type FruitArray = Fruit[];

const someFruits= ["Banana"];

const workingFruits: FruitArray = ["Orange", "Apple"]; // Works

// Even orange and apple show: Type 'string' is not assignable to type Fruit
const brokenAllFruits: FruitArray = [...someFruits, "Orange", "Apple"]; 

// As const is needed in the spread array
const someConstFruits= ["Banana" as const];
const workingAllFruits: FruitArray = [...someConstFruits, "Orange", "Apple"]; // Works
person blub    schedule 17.04.2021

Если вы выполняете преобразование в dropdownvalue[] при насмешке данных, например, составьте его как массив объектов со свойствами значения и отображения.

пример:

[{'value': 'test1', 'display1': 'test display'},{'value': 'test2', 'display': 'test display2'},]
person meol    schedule 01.08.2019

Я получал то же сообщение об ошибке с немного другим сценарием. Я пришел сюда в поисках ответов, но ни один из ответов у меня не помог, поэтому я поделюсь своим решением с другими. У меня не было специального типа, это был просто массив строк. У меня был объект myObject со строковым свойством abcOrD, которое должно было быть одним из значений в массиве строк, например a b c или d. При попытке назначить myObject.abcOrD = myStringVar я получаю сообщение об ошибке Type 'string' is not assignable to type "a" | "b" | "c" | "d". Поигравшись и попробовав кое-что, я использовал как любой:

myObject.abcOrD = myStringVar as any;
person SendETHToThisAddress    schedule 05.03.2021

Я столкнулся с той же проблемой, я внес изменения ниже, и проблема была решена.

Откройте файл watchQueryOptions.d.ts.

\apollo-client\core\watchQueryOptions.d.ts

Измените тип запроса любой вместо DocumentNode, то же самое для мутации.

До:

export interface QueryBaseOptions<TVariables = OperationVariables> {
    query: **DocumentNode**;

После:

export interface QueryBaseOptions<TVariables = OperationVariables> {
    query: **any**;
person Anand N    schedule 23.08.2019