Kiểu chữ 'chuỗi' không thể gán cho kiểu


161

Đây là những gì tôi có trong fruit.ts

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

Bây giờ tôi đang nhập fruit.ts trong một tệp bản thảo khác. Đây là những gì tôi có

myString:string = "Banana";

myFruit:Fruit = myString;

Khi tôi làm

myFruit = myString;

Tôi gặp lỗi:

Loại 'chuỗi' không thể gán cho loại '"Cam" | "Táo" | "Trái chuối"'

Làm cách nào tôi có thể gán một chuỗi cho một biến loại Fruit tùy chỉnh?


1
Bạn có chắc chắn về việc sử dụng dấu ngoặc đơn và dấu ngoặc kép trong export type Fruit?
Thời tiết Vane

1
@WeatherVane Chỉ cần kiểm tra Fruit.ts của tôi. Tôi có một dấu ngoặc đơn trong đó cho loại xuất khẩu Fruit = 'Orange' || 'Táo "||' Chuối". Cảm ơn bạn
user6123723

Vẫn giống như một số trích dẫn kép đối với tôi ...
Weather Vane

Câu trả lời:


231

Bạn sẽ cần bỏ nó :

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

let myFruit: Fruit = myString as Fruit;

Cũng lưu ý rằng khi sử dụng chuỗi ký tự, bạn chỉ cần sử dụng một|

Biên tập

Như @Simon_Weaver đã đề cập trong câu trả lời khác, giờ đây có thể khẳng định nó là const:

let fruit = "Banana" as const;

11
tốt đẹp :) trong hầu hết các trường hợp const myFruit: Fruit = "Banana"sẽ làm.
Jacka

Tôi có thể làm ngược lại không? Ý tôi là một cái gì đó như thế này let myFruit:Fruit = "Apple" let something:string = myFruit as string Nó gây ra lỗi cho tôi: Chuyển đổi loại 'Trái cây' sang loại 'chuỗi' có thể là một sai lầm.
Siraj Alam

@Siraj Nó sẽ hoạt động tốt, thậm chí bạn không cần as stringmột phần. Tôi đã thử mã của bạn trong sân chơi và không có lỗi.
Nitzan Tomer

@NitzanTomer stackoverflow.com/questions/53813188/ Từ Hãy xem câu hỏi chi tiết của tôi
Siraj Alam

Nhưng bây giờ nếu tôi đánh máy const myString: string = 'Bananaaa'; tôi không gặp lỗi biên dịch vì quá trình truyền ... có cách nào để làm điều này trong khi gõ kiểm tra chuỗi không?
blub

66

Bản đánh máy 3.4giới thiệu khẳng định 'const' mới

Bây giờ bạn có thể ngăn các loại chữ (ví dụ: 'orange'hoặc 'red') được 'mở rộng' để nhập stringvới một constxác nhận được gọi là xác nhận.

Bạn sẽ có thể làm:

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

Và sau đó nó sẽ không biến mình thành một stringvấn đề nữa - đó là gốc rễ của vấn đề trong câu hỏi.


Đối với những người, giống như tôi, chưa có ngày 3,4. <const> có thể được thay thế bằng bất kỳ, nhưng tất nhiên là không sạch như giải pháp này.
Pieter De Bie

2
Cú pháp được ưu tiên sẽ là let fruit = 'orange' as const;khi tuân theo quy tắc xác nhận không có khung góc-khung
ThunderDev

2
Đây là câu trả lời đúng cho Bản đánh máy hiện đại. Nó ngăn chặn việc nhập các loại không cần thiết và cho phép gõ cấu trúc thực hiện chính xác.
James McMahon

24

Khi bạn làm điều này:

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

... bạn đang tạo một loại gọi Fruitmà chỉ có thể chứa các chữ "Orange", "Apple""Banana". Loại này mở rộng String, do đó nó có thể được gán cho String. Tuy nhiên, StringKHÔNG mở rộng "Orange" | "Apple" | "Banana", vì vậy nó không thể được gán cho nó. Stringít cụ thể . Nó có thể là bất kỳ chuỗi .

Khi bạn làm điều này:

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

const myString = "Banana";

const myFruit: Fruit = myString;

... nó hoạt động. Tại sao? Bởi vì thực tế loại của myStringtrong ví dụ này là "Banana". Vâng, "Banana"loại . Nó được mở rộng Stringđể nó có thể được gán String. Ngoài ra, một loại mở rộng Loại Liên minh khi nó mở rộng bất kỳ thành phần nào của nó. Trong trường hợp này, "Banana"loại, mở rộng "Orange" | "Apple" | "Banana"vì nó mở rộng một trong các thành phần của nó. Do đó, "Banana"được gán cho "Orange" | "Apple" | "Banana"hoặc Fruit.


2
Thật buồn cười khi bạn thực sự có thể đặt <'Banana'> 'Banana'và điều đó sẽ 'đúc' một "Banana"chuỗi "Banana"thành loại !!!
Simon_Weaver

2
Nhưng bây giờ bạn có thể làm điều <const> 'Banana'đó tốt hơn :-)
Simon_Weaver

11

Tôi thấy điều này hơi cũ, nhưng có thể có một giải pháp tốt hơn ở đây.

Khi bạn muốn một chuỗi, nhưng bạn muốn chuỗi chỉ khớp với các giá trị nhất định, bạn có thể sử dụng enums .

Ví dụ:

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

let myFruit: Fruit = Fruit.Banana;

Bây giờ bạn sẽ biết rằng không có vấn đề gì, myFnut sẽ luôn là chuỗi "Banana" (Hoặc bất kỳ giá trị vô số nào khác bạn chọn). Điều này hữu ích cho nhiều thứ, cho dù đó là nhóm các giá trị tương tự như thế này hay ánh xạ các giá trị thân thiện với người dùng thành các giá trị thân thiện với máy, tất cả trong khi thực thi và hạn chế các giá trị mà trình biên dịch sẽ cho phép.


1
Thật buồn cười vì tôi gặp phải vấn đề này khi cố gắng tránh xa việc này. Nó ngày càng khiến tôi phát điên. Tôi đang cố gắng trở thành 'javascripty' và sử dụng các chuỗi ma thuật bị ràng buộc theo một loại (thay vì liệt kê) và nó dường như phản tác dụng ngày càng nhiều và gợn qua toàn bộ ứng dụng của tôi với lỗi này: - /
Simon_Weaver

1
Điều này cũng gây ra vấn đề ngược lại. Bạn không thể làm được nữa let myFruit: Fruit = "Banana".
Sean Burton

11

Có một số tình huống sẽ cung cấp cho bạn lỗi đặc biệt này. Trong trường hợp của OP, có một giá trị được xác định rõ ràng là một chuỗi . Vì vậy, tôi phải giả định rằng có thể điều này xuất phát từ một danh sách thả xuống, hoặc dịch vụ web hoặc chuỗi JSON thô.

Trong trường hợp đó, một diễn viên đơn giản <Fruit> fruitStringhoặc fruitString as Fruitlà giải pháp duy nhất (xem các câu trả lời khác). Bạn sẽ không bao giờ có thể cải thiện điều này vào thời gian biên dịch. [ Chỉnh sửa: Xem câu trả lời khác của tôi về<const> ]!

Tuy nhiên, rất dễ gặp phải lỗi tương tự khi sử dụng các hằng số trong mã của bạn mà không bao giờ có ý định thuộc loại chuỗi . Câu trả lời của tôi tập trung vào kịch bản thứ hai đó:


Trước hết: Tại sao hằng số chuỗi 'ma thuật' thường tốt hơn enum?

  • Tôi thích cách một chuỗi liên tục trông so với enum - nó nhỏ gọn và 'javascripty'
  • Có ý nghĩa hơn nếu thành phần bạn đang sử dụng đã sử dụng hằng chuỗi.
  • Phải nhập một loại 'enum' chỉ để có được giá trị liệt kê có thể gây rắc rối cho chính nó
  • Bất cứ điều gì tôi làm tôi muốn nó được biên dịch an toàn vì vậy nếu tôi thêm loại bỏ một giá trị hợp lệ khỏi loại kết hợp hoặc gõ nhầm thì nó PHẢI đưa ra lỗi biên dịch.

May mắn thay khi bạn xác định:

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

... Bạn đang thực sự xác định một tập hợp các loại trong đó 'missing'thực sự là một loại!

Tôi thường gặp phải lỗi 'không thể gán được' nếu tôi có một chuỗi như 'banana'trong bản thảo của mình và trình biên dịch nghĩ rằng tôi có nghĩa đó là một chuỗi, trong khi tôi thực sự muốn nó thuộc loại banana. Trình biên dịch có thể thông minh đến mức nào sẽ phụ thuộc vào cấu trúc mã của bạn.

Đây là một ví dụ về khi tôi gặp lỗi này ngày hôm nay:

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

Ngay khi tôi phát hiện ra rằng 'invalid'hoặc 'banana'có thể là một loại hoặc một chuỗi, tôi nhận ra rằng tôi chỉ có thể xác nhận một chuỗi vào loại đó . Về cơ bản, truyền nó cho chính nó và nói với trình biên dịch rằng tôi không muốn đây là một chuỗi !

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

Vì vậy, có gì sai khi chỉ 'đúc' thành FieldErrorType(hoặc Fruit)

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

Nó không biên dịch thời gian an toàn:

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

Tại sao? Đây là bản đánh máy nên đây <FieldErrorType>là một khẳng định và bạn đang nói với trình biên dịch một con chó là FieldErrorType ! Và trình biên dịch sẽ cho phép nó!

NHƯNG nếu bạn làm như sau, thì trình biên dịch sẽ chuyển đổi chuỗi thành một kiểu

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

Chỉ cần coi chừng những lỗi chính tả ngu ngốc như thế này:

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

Một cách khác để giải quyết vấn đề là bằng cách truyền đối tượng cha:

Định nghĩa của tôi là như sau:

loại xuất FieldName = 'số' | 'Hết hạn' | 'cvv'; loại xuất FieldError = 'none' | 'mất tích' | 'không hợp lệ'; kiểu xuất FieldErrorType = {field: FieldName, error: FieldError};

Giả sử chúng ta gặp lỗi với lỗi này (chuỗi không thể gán lỗi):

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

Chúng ta có thể 'khẳng định' toàn bộ đối tượng như FieldErrorTypethế này:

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

Sau đó, chúng tôi tránh phải làm <'invalid'> 'invalid'.

Nhưng còn lỗi chính tả thì sao? Không <FieldErrorType>chỉ khẳng định bất cứ điều gì có quyền thuộc loại đó. Không phải trong trường hợp này - may mắn là trình biên dịch S phàn nàn nếu bạn làm điều này, bởi vì nó đủ thông minh để biết điều đó là không thể:

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

Có thể có sự tinh tế với chế độ nghiêm ngặt. Sẽ cập nhật câu trả lời sau khi xác nhận.
Simon_Weaver

1

Tất cả các câu trả lời ở trên là hợp lệ, tuy nhiên, có một số trường hợp Loại Chuỗi chữ là một phần của loại phức tạp khác. Hãy xem xét ví dụ sau:

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

Bạn có nhiều giải pháp để khắc phục điều này. Mỗi giải pháp là hợp lệ và có trường hợp sử dụng riêng.

1) Giải pháp đầu tiên là xác định loại cho kích thước và xuất nó từ foo.ts. Điều này là tốt nếu khi bạn cần phải làm việc với tham số kích thước của chính nó. Ví dụ: bạn có một hàm chấp nhận hoặc trả về một tham số có kích thước loại và bạn muốn nhập nó.

  // 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) Tùy chọn thứ hai là chỉ chuyển nó sang loại Thanh công cụ. Trong trường hợp này, bạn không cần phải tiết lộ nội bộ của Thanh công cụ nếu bạn không cần.

  // 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[]Ví dụ, nếu bạn đang truyền dữ liệu khi chế nhạo dữ liệu, hãy kết hợp nó thành một mảng các đối tượng có các thuộc tính giá trị và hiển thị.

ví dụ :

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

0

Tôi đã phải đối mặt với cùng một vấn đề, tôi đã thực hiện các thay đổi dưới đây và vấn đề đã được giải quyết.

Mở tệp watchQueryOptions.d.ts

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

Thay đổi loại truy vấn bất kỳ thay vì DocumentNode , Tương tự cho đột biến

Trước:

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

Sau:

export interface QueryBaseOptions<TVariables = OperationVariables> {
    query: **any**;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.