typescript Type 'string' ไม่สามารถกำหนดให้พิมพ์ได้


161

นี่คือสิ่งที่ฉันมีใน fruit.ts

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

ตอนนี้ฉันกำลังนำเข้า fruit.ts ในไฟล์ typescript อื่น นี่คือสิ่งที่ฉันมี

myString:string = "Banana";

myFruit:Fruit = myString;

เมื่อฉันทำ

myFruit = myString;

ฉันได้รับข้อผิดพลาด:

ประเภท 'string' ไม่สามารถกำหนดให้พิมพ์ "" Orange "| "Apple" | "กล้วย"'

ฉันจะกำหนดสตริงให้กับตัวแปรประเภทผลไม้ที่กำหนดเองได้อย่างไร


1
คุณค่อนข้างแน่ใจเกี่ยวกับการใช้คำพูดเดี่ยวและคู่export type Fruitใช่หรือไม่?
ใบพัดสภาพอากาศ

1
@ WeatherVane เพียงตรวจสอบ Fruit.ts ของฉัน ฉันมีเครื่องหมายคำพูดเดี่ยวสำหรับประเภทการส่งออก Fruit = 'Orange' || 'Apple "||' Banana" ขอบคุณ
user6123723

ยังดูเหมือนว่าคำพูดคู่บางอย่างเพื่อฉัน ...
อากาศพัดลม

คำตอบ:


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 Alam

@Siraj มันน่าจะใช้ได้ดีคุณไม่จำเป็นต้องมีas stringส่วน ฉันลองใช้รหัสในสนามเด็กเล่นแล้วและไม่มีข้อผิดพลาด
Nitzan Tomer

@NitzanTomer stackoverflow.com/questions/53813188/…โปรดดูคำถามโดยละเอียดของฉัน
Siraj Alam

แต่ตอนนี้ถ้าฉันพิมพ์ 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> สามารถถูกแทนที่ด้วยสิ่งใดก็ได้ แต่แน่นอนว่าไม่สะอาดเหมือนโซลูชันนี้
Pieter De Bie

2
ไวยากรณ์ที่ต้องการจะเป็นlet fruit = 'orange' as const;เมื่อทำตามกฎไม่มีมุมวงเล็บชนิดตามใจ
ThunderDev

2
นี่คือคำตอบที่ถูกต้องสำหรับ typescript ที่ทันสมัย มันช่วยป้องกันการนำเข้าประเภทที่ไม่จำเป็นและช่วยให้การพิมพ์โครงสร้างทำได้อย่างถูกต้อง
James McMahon

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ได้ นอกจากนี้ชนิดจะขยายประเภทสหภาพเมื่อขยายส่วนประกอบใด ๆ ในกรณีนี้"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' และใช้เวทสตริงที่ จำกัด ประเภท (แทนที่จะนับ) และดูเหมือนว่าจะย้อนกลับมามากขึ้นเรื่อย ๆ และกระเพื่อมผ่านแอปพลิเคชันทั้งหมดของฉันด้วยข้อผิดพลาดนี้: - /
Simon_Weaver

1
สิ่งนี้ยังทำให้เกิดปัญหาตรงกันข้าม let myFruit: Fruit = "Banana"คุณไม่สามารถทำได้
ฌอนเบอร์ตัน

11

มีหลายสถานการณ์ที่จะทำให้คุณเกิดข้อผิดพลาดนี้ ในกรณีของ OP มีการกำหนดค่าไว้อย่างชัดเจนว่าเป็นสตริงที่กำหนดไว้อย่างชัดเจนเป็นสตริงดังนั้นฉันต้องสมมติว่าอาจมาจากการเลื่อนลงหรือบริการเว็บหรือสตริง JSON ดิบ

ในกรณีดังกล่าวการส่งแบบง่าย<Fruit> fruitStringหรือfruitString as Fruitเป็นการแก้ปัญหาเพียงอย่างเดียว (ดูคำตอบอื่น ๆ ) คุณจะไม่สามารถปรับปรุงได้ในเวลารวบรวม [ แก้ไข: ดูคำตอบอื่น ๆ ของฉันเกี่ยวกับ<const> ]!

แต่มันเป็นเรื่องง่ายมากที่จะใช้เป็นข้อผิดพลาดเดียวกันนี้เมื่อใช้ค่าคงที่ในรหัสของคุณที่จะไม่เคยตั้งใจที่จะเป็นชนิดสตริง คำตอบของฉันมุ่งเน้นไปที่สถานการณ์ที่สอง:


ก่อนอื่น: ทำไมค่าคงที่สตริง 'วิเศษ' จึงมักจะดีกว่าค่า enum?

  • ฉันชอบวิธีที่ค่าคงที่สตริงดูเทียบกับ enum - มันกะทัดรัดและ 'javascripty'
  • เหมาะสมกว่าหากองค์ประกอบที่คุณใช้อยู่แล้วใช้ค่าคงที่สตริง
  • การต้องนำเข้า 'ประเภท enum' เพียงเพื่อให้ได้ค่าการแจงนับอาจมีปัญหาในตัวเอง
  • สิ่งที่ฉันต้องการฉันจะรวบรวมความปลอดภัยดังนั้นหากฉันเพิ่มลบค่าที่ถูกต้องจากประเภทสหภาพหรือพิมพ์ผิดมันก็ต้องให้ข้อผิดพลาดในการรวบรวม

โชคดีเมื่อคุณกำหนด:

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 = {field: 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 สิ่งนี้จะดีถ้าเมื่อคุณต้องการทำงานกับพารามิเตอร์ 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 ในกรณีนี้คุณไม่จำเป็นต้องเปิดเผยภายในของ 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)
  ));

0

หากคุณกำลังส่งไปยังตัวอย่างdropdownvalue[]เมื่อทำการเยาะเย้ยข้อมูลให้เขียนเป็นอาร์เรย์ของวัตถุที่มีค่าและคุณสมบัติการแสดงผล

ตัวอย่าง :

[{'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**;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.