[javascript] Typescript Type ‘string’은 (는) type에 할당 할 수 없습니다

여기 내가 과일에 가지고있는 것이 있습니다.

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

이제 다른 typescript 파일로 fruit.ts를 가져오고 있습니다. 여기 내가 가진 것

myString:string = "Banana";

myFruit:Fruit = myString;

내가 할 때

myFruit = myString;

오류가 발생합니다.

‘string’유형은 ‘ “Orange”유형에 지정할 수 없습니다. | “애플”| “바나나”‘

사용자 정의 유형 Fruit 변수에 문자열을 할당하려면 어떻게해야합니까?



답변

당신은 그것을 캐스팅해야합니다 :

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

let myFruit: Fruit = myString as Fruit;

또한 문자열 리터럴 을 사용할 때는 하나만 사용해야합니다.|

편집하다

@Simon_Weaver의 다른 답변에서 언급했듯이 이제는 다음과 같이 주장 할 수 있습니다 const.

let fruit = "Banana" as const;


답변

Typescript 3.4는 새로운 ‘const’주장을 소개합니다

이제 리터럴 유형 (예 : 'orange'또는 'red')이 string소위 const주장 으로 입력하기 위해 ‘확대’되는 것을 방지 할 수 있습니다 .

당신은 할 수있을 것입니다 :

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

그리고 그것은 string더 이상 그 자체로 변하지 않을 것입니다 -이것이 문제의 근본 원인입니다.


답변

이렇게하면 :

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"는 IS 유형 . 확장 가능 String하여에 할당 할 수 String있습니다. 또한 유형 구성 요소를 확장 때 결합 유형을 확장 합니다 . 이 경우 "Banana"유형 "Orange" | "Apple" | "Banana"은 구성 요소 중 하나를 확장 하므로 확장됩니다. 따라서 또는에 "Banana"할당 할 수 있습니다."Orange" | "Apple" | "Banana"Fruit


답변

나는 이것이 조금 오래된 것을 보았지만 여기에 더 나은 해결책이있을 수 있습니다.

문자열을 원하지만 문자열이 특정 값과 만 일치하도록하려면 enums 를 사용할 수 있습니다 .

예를 들면 다음과 같습니다.

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

let myFruit: Fruit = Fruit.Banana;

이제 무엇이든 myFruit은 항상 문자열 “Banana”(또는 열거 가능한 다른 값)가 될 것입니다. 이것은 컴파일러가 허용하는 값을 적용하고 제한하면서 이와 유사한 값을 그룹화하거나 사용자 친화적 인 값을 기계 친화적 인 값으로 매핑하는 등 많은 것들에 유용합니다.


답변

이 특정 오류를 발생시키는 몇 가지 상황이 있습니다. OP의 경우 명시 적으로 문자열로 정의 된 값이있었습니다 . 따라서 이것은 드롭 다운 또는 웹 서비스 또는 원시 JSON 문자열에서 비롯된 것으로 가정해야합니다.

이 경우 간단한 캐스트 <Fruit> fruitString또는 fruitString as Fruit유일한 해결책입니다 (다른 답변 참조). 컴파일 타임에는 이것을 향상시킬 수 없습니다. [ 편집 :에 대한 다른 답변보기<const> ]!

그러나 코드에서 문자열 유형이 아닌 상수를 사용할 때이 동일한 오류가 발생하기 쉽습니다 . 내 대답은 두 번째 시나리오에 중점을 둡니다.


우선 : 왜 ‘매직’문자열 상수가 종종 열거 형보다 낫습니까?

  • 나는 문자열 상수가 열거 형과 열거 형을 좋아합니다-컴팩트하고 ‘자바 스크립트’
  • 사용중인 구성 요소가 이미 문자열 상수를 사용하는 경우 더 의미가 있습니다.
  • 열거 형 값을 얻기 위해 ‘enum type’을 가져와야하는 것은 그 자체로 까다로울 수 있습니다
  • 내가 무엇을하든 컴파일 안전 하기를 원하므로 공용체 유형에서 유효한 값을 제거하거나 잘못 입력하면 컴파일 오류가 발생해야합니다.

다행히도 다음을 정의 할 때

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

… 당신은 실제로 정의하고 유형의 조합'missing' 실제로 타입입니다!

필자는 'banana'typescript와 같은 문자열이 있고 컴파일러 문자열을 의미 한다고 생각 하는 경우 ‘할당 할 수 없음’오류가 종종 발생 하지만 실제로는 유형이되기를 원했습니다 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!

왜? 이것은 typescript이므로 <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 = {필드 : FieldName, 오류 : FieldError};

이 오류가 발생한다고 가정합니다 (할당 할 수없는 문자열).

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

전체 객체를 다음 FieldErrorType과 같이 ‘어설 션’할 수 있습니다 .

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

그렇다면 우리는 할 필요가 없습니다 <'invalid'> 'invalid'.

그러나 오타는 어떻습니까? 하지 않습니다 <FieldErrorType>단지 주장 유형이 될 수있는 권리에 무엇이든. 이 경우에는 아닙니다. 다행스럽게도 컴파일러 는 불가능하다는 것을 알기에 충분히 영리하기 때문에 이렇게하면 불평 할 것입니다 .

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


답변

위의 모든 대답은 유효하지만 문자열 리터럴 유형이 다른 복잡한 유형의 일부인 경우가 있습니다. 다음 예제를 고려하십시오.

  // 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에서 내보내는 것입니다. size 매개 변수를 자체적으로 사용해야하는 경우에 좋습니다. 예를 들어, size 유형의 매개 변수를 승인하거나 리턴하는 함수가 있으며이를 입력하려고합니다.

  // 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 내부를 노출 할 필요가 없습니다.

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


답변

dropdownvalue[]예를 들어 데이터를 조롱 할 때에 캐스팅하는 경우 값 및 표시 속성이있는 객체의 배열로 데이터를 작성하십시오.

:

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