Typescript Тип 'string' не можна присвоювати типу


161

Ось що у мене є у fru.ts

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

Зараз я імпортую файл fru.ts в інший файл машинопису. Ось що я маю

myString:string = "Banana";

myFruit:Fruit = myString;

Коли я це роблю

myFruit = myString;

Я отримую помилку:

Тип 'рядок' не призначається типу "помаранчевий" | "Яблуко" | "Банан"

Як я можу призначити рядок змінній користувальницького типу Fruit?


1
Ви впевнені у використанні одиничних та подвійних лапок у export type Fruit?
— Погода Ване

1
@WeatherVane Щойно перевірив мій Fruit.ts. У мене є єдині котирування на експорт типу Fruit = 'Orange' || 'Яблуко' || 'банан'. Дякую
— користувач6123723

Досі виглядає як подвійні цитати для мене ...
— Weather Vane

Відповіді:


231

Вам потрібно буде віддати це :

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

let myFruit: Fruit = myString as Fruit;

Також зауважте, що при використанні рядкових літералів вам потрібно використовувати лише один|

Редагувати

Як було сказано в іншій відповіді @Simon_Weaver, тепер це можна стверджувати const:

let fruit = "Banana" as const;

11
приємно :) У більшості випадків const myFruit: Fruit = "Banana"це зробить.
— Jacka

Чи можу я зробити її зворотним? Я маю на увазі щось подібне. let myFruit:Fruit = "Apple" let something:string = myFruit as string Це дає мені помилку: перетворення типу 'фрукт' у тип 'рядок' може бути помилкою.
— Сірай Алам

@Siraj Це повинно працювати чудово, as stringчастина вам навіть не потрібна . Я спробував ваш код на дитячій площадці, і помилок немає.
— Нітзан Томер

@NitzanTomer stackoverflow.com/questions/53813188 / ... Будь ласка , подивіться на мій детальний питання
— Сирай Алам

Але тепер, якщо я вводячи помилку, const myString: string = 'Bananaaa'; я не отримую помилок компіляції через кастинг ... чи немає способу це зробити під час введення перевірки рядка?
— блю

66

Typescript 3.4вводить нове твердження "const"

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

Ви зможете:

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

І тоді вона вже не перетвориться на stringбільше - що є коренем проблеми у питанні.


Для людей, як і я, ще немає 3,4. <const> можна замінити будь-яким, але, звичайно, не такий чистий, як це рішення.
— Пітер Де Бі

2
Переважним буде синтаксис, let fruit = 'orange' as const;коли слід дотримуватись правила затвердження типу no-angle-скоба
— ThunderDev

2
Це правильна відповідь для сучасного Typescript. Це перешкоджає непотрібному імпорту типів і дозволяє структурному введенню робити свою справу правильно.
— Джеймс Макмахон

24

Коли ви це зробите:

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. Крім того, тип розширює тип Union, коли він розширює будь-який з його компонентів. У цьому випадку "Banana"тип, поширюється, "Orange" | "Apple" | "Banana"оскільки розширює один із його компонентів. Отже, "Banana"присвоюється "Orange" | "Apple" | "Banana"або Fruit.


2
Це смішно, що ви можете насправді поставити, <'Banana'> 'Banana'і це буде "кидати" "Banana"рядок "Banana"типу !!!
— Simon_Weaver

2
Але тепер ви можете зробити <const> 'Banana'що краще :-)
— Simon_Weaver

11

Я бачу, це трохи старе, але тут може бути краще рішення.

Коли ви хочете, щоб рядок, але ви хочете, щоб рядок відповідав лише певним значенням, ви можете використовувати enums .

Наприклад:

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

let myFruit: Fruit = Fruit.Banana;

Тепер ви будете знати, що незалежно від того, myFruit завжди буде рядком "Banana" (або будь-яке інше чисельне значення, яке ви виберете). Це корисно для багатьох речей, будь то групування подібних значень, як це, або зіставлення значень, зручних для користувачів, до дружнього для машинних значень, і все це застосовувати та обмежувати значення, які компілятор дозволить.


1
Це смішно, бо я отримую це питання, коли намагаюся втекти від цього. Це все частіше мене заганяє. Я намагаюся бути "javascripty" і використовую магічні рядки, обмежені типом (замість перерахунку), і, здається, все більше і більше
— буває зворотне ввімкнення

1
Це також викликає протилежну проблему. Більше не можна робити let myFruit: Fruit = "Banana".
— Шон Бертон

11

Є кілька ситуацій, які дадуть вам саме цю помилку. У випадку з ОП було значення, визначене явно як рядок . Тож я маю припустити, що, можливо, це сталося із спадного меню, веб-сервісу чи необробленої рядки JSON.

У цьому випадку простий склад <Fruit> fruitStringабо fruitString as Fruitєдине рішення (див. Інші відповіді). Ви ніколи не зможете покращити це під час компіляції. [ Редагувати: див. Мою іншу відповідь про<const> ]!

Однак зіткнутися з цією самою помилкою дуже просто при використанні констант у вашому коді, які ніколи не повинні мати тип рядка . Моя відповідь зосереджена на тому другому сценарії:


Перш за все: чому "магічні" струнні константи часто кращі за перерахунок?

  • Мені подобається, як струнна константа виглядає проти перерахунку - вона компактна і "javascripty"
  • Має більше сенсу, якщо компонент, який ви використовуєте, вже використовує рядкові константи.
  • Необхідність імпортувати "enum type" лише для отримання значення перерахування може бути проблемою сама по собі
  • Що б я не робив, я хочу, щоб воно було безпечним для компіляції, тому якщо я додаю видалити дійсне значення з типу об'єднання або введіть його помилково, то воно ОБОВ'ЯЗКОВО подасть помилку компіляції.

На щастя, коли ви визначаєте:

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 = 'число' | 'дата закінчення' | 'cvv'; тип експорту FieldError = 'немає' | 'відсутній' | 'недійсний'; тип експорту FieldErrorType = {поле: FieldName, помилка: FieldError};

Скажімо, ми отримуємо помилку з цим (рядок не присвоюється помилка):

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

Ми можемо «стверджувати» весь об’єкт так FieldErrorType:

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

Тоді ми уникаємо цього робити <'invalid'> 'invalid'.

А як щодо помилок друку? Це не <FieldErrorType>просто стверджує, що є право на право такого типу. Не в цьому випадку - на щастя, компілятор ДАЄ поскаржитися, якщо ви це зробите, оскільки це досить розумно, щоб знати, що це неможливо:

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

Можуть бути тонкощі при строгому режимі. Відповідь буде оновлено після підтвердження.
— Simon_Weaver

1

Усі вищезазначені відповіді справедливі, проте є випадки, що тип рядкового літералу є частиною іншого складного типу. Розглянемо наступний приклад:

  // 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) Другий варіант - просто передати його типу TypebarTheme. У цьому випадку вам не потрібно розкривати внутрішню панель інструментів, якщо вона вам не потрібна.

  // 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)
  ));

0

Якщо ви, наприклад, під dropdownvalue[]час макетування даних передаваєте на a , складіть його як масив об'єктів зі значеннями та властивостями відображення.

приклад :

[{'value': 'test1', 'display1': 'test display'},{'value': 'test2', 'display': 'test display2'},]

0

Я зіткнувся з тією ж проблемою, я вніс зміни, і це питання було вирішено.

Відкрийте файл watchQueryOptions.d.ts

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

Змініть тип запиту будь-якої замість DocumentNode , То ж саме для мутації

Перед:

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

Після:

export interface QueryBaseOptions<TVariables = OperationVariables> {
    query: **any**;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.