Naskah Jenis 'string' tidak dapat ditentukan untuk diketik

162

Inilah yang saya miliki di fruit.ts

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

Sekarang saya mengimpor fruit.ts dalam file naskah lain. Inilah yang saya miliki

myString:string = "Banana";

myFruit:Fruit = myString;

Kapan saya melakukannya

myFruit = myString;

Saya mendapatkan kesalahan:

Ketik 'string' tidak dapat ditentukan untuk mengetik '"Oranye" | "Apple" | "Pisang"'

Bagaimana saya bisa menetapkan string ke variabel jenis kustom Buah?

pengguna6123723
sumber
1
Apakah Anda cukup yakin tentang penggunaan tanda kutip tunggal dan ganda export type Fruit?
Weather Vane
1
@WeatherVane Baru saja memeriksa Fruit.ts saya. Saya memiliki tanda kutip tunggal di sana untuk jenis ekspor Fruit = 'Orange' || 'Apple "||' Banana". Terima kasih
user6123723
Masih terlihat seperti beberapa tanda kutip ganda bagi saya ...
Weather Vane

Jawaban:

231

Anda harus membuatnya :

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

let myFruit: Fruit = myString as Fruit;

Juga perhatikan bahwa ketika menggunakan string literal Anda hanya perlu menggunakan satu|

Edit

Seperti disebutkan dalam jawaban lain oleh @Simon_Weaver, sekarang mungkin untuk menegaskannya ke const:

let fruit = "Banana" as const;
Nitzan Tomer
sumber
11
bagus :) dalam banyak kasus const myFruit: Fruit = "Banana"akan dilakukan.
Jacka
Bisakah saya melakukan kebalikannya? Maksud saya sesuatu seperti ini let myFruit:Fruit = "Apple" let something:string = myFruit as string Ini memberi saya kesalahan: Konversi tipe 'Buah' ke tipe 'string' mungkin merupakan kesalahan.
Siraj Alam
@ Siraj Ini seharusnya bekerja dengan baik, Anda bahkan tidak perlu as stringbagian itu. Saya sudah mencoba kode Anda di taman bermain dan tidak ada kesalahan.
Nitzan Tomer
@NitzanTomer stackoverflow.com/questions/53813188/... Silakan lihat pertanyaan terperinci saya
Siraj Alam
Tetapi sekarang jika saya salah ketik const myString: string = 'Bananaaa'; saya tidak mendapatkan kesalahan kompilasi karena casting ... apakah tidak ada cara untuk melakukan ini saat mengetik memeriksa string?
blub
67

Scriptants 3.4memperkenalkan pernyataan 'const' yang baru

Anda sekarang dapat mencegah tipe literal (mis. 'orange'Atau 'red') 'diperlebar' untuk mengetik stringdengan apa yang disebut constpernyataan tegas.

Anda akan dapat melakukan:

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

Dan kemudian itu tidak akan berubah menjadi stringlagi - yang merupakan akar masalah dalam pertanyaan.

Simon_Weaver
sumber
Untuk orang-orang yang, seperti saya, belum menggunakan 3.4. <const> bisa diganti oleh apa saja, tetapi tentu saja tidak sebersih solusi ini.
Pieter De Bie
2
Sintaks prefered akan let fruit = 'orange' as const;ketika mengikuti aturan no-sudut-braket-jenis-penegasan
ThunderDev
2
Ini adalah jawaban yang tepat untuk naskah modern. Ini mencegah impor tipe yang tidak dibutuhkan dan memungkinkan pengetikan struktural melakukan hal tersebut dengan benar.
James McMahon
24

Ketika Anda melakukan ini:

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

... Anda membuat tipe yang disebut Fruityang hanya bisa berisi literal "Orange", "Apple"dan "Banana". Tipe ini meluas String, karenanya dapat ditugaskan untuk String. Namun, StringTIDAK meluas "Orange" | "Apple" | "Banana", sehingga tidak dapat ditugaskan untuk itu. Stringadalah kurang spesifik . Itu bisa berupa string apa saja .

Ketika Anda melakukan ini:

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

const myString = "Banana";

const myFruit: Fruit = myString;

...berhasil. Mengapa? Karena sebenarnya jenis dari myStringdalam contoh ini adalah "Banana". Ya, "Banana"adalah tipenya . Itu meluas Stringjadi itu ditugaskan untuk String. Selain itu, suatu jenis memperluas Jenis Serikat saat meluas apa pun satu komponennya. Dalam hal ini, "Banana"tipe tersebut memanjang "Orange" | "Apple" | "Banana"karena memanjang salah satu komponennya. Oleh karena itu, "Banana"ditugaskan untuk "Orange" | "Apple" | "Banana"atau Fruit.

André Pena
sumber
2
Ini lucu Anda benar-benar dapat menempatkan <'Banana'> 'Banana'dan yang akan 'melemparkan' "Banana"string ke "Banana"tipe !!!
Simon_Weaver
2
Tapi sekarang Anda bisa melakukan <const> 'Banana'yang lebih baik :-)
Simon_Weaver
11

Saya melihat ini agak lama, tetapi mungkin ada solusi yang lebih baik di sini.

Saat Anda menginginkan string, tetapi Anda ingin string hanya cocok dengan nilai-nilai tertentu, Anda bisa menggunakan enum .

Sebagai contoh:

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

let myFruit: Fruit = Fruit.Banana;

Sekarang Anda akan tahu bahwa apa pun yang terjadi, myFruit akan selalu menjadi string "Banana" (Atau nilai enumerable apa pun yang Anda pilih). Ini berguna untuk banyak hal, apakah itu mengelompokkan nilai yang sama seperti ini, atau memetakan nilai yang ramah pengguna ke nilai yang ramah mesin, semuanya sambil menegakkan dan membatasi nilai yang diizinkan oleh kompiler.

Steve Adams
sumber
1
Ini lucu karena saya mendapatkan masalah ini ketika mencoba melarikan diri dari melakukan ini. Ini semakin membuatku gila. Saya mencoba menjadi 'javascripty' dan menggunakan string ajaib yang dibatasi pada tipe (bukan enumerasi) dan tampaknya menjadi bumerang semakin banyak dan mengacaukan seluruh aplikasi saya dengan kesalahan ini: - /
Simon_Weaver
1
Ini juga menyebabkan masalah sebaliknya. Anda tidak dapat lagi melakukan let myFruit: Fruit = "Banana".
Sean Burton
11

Ada beberapa situasi yang akan memberi Anda kesalahan khusus ini. Dalam kasus OP ada nilai yang didefinisikan secara eksplisit sebagai string . Jadi saya harus berasumsi bahwa mungkin ini berasal dari dropdown, atau layanan web atau string JSON mentah.

Dalam hal ini pemeran sederhana <Fruit> fruitStringatau fruitString as Fruitsatu-satunya solusi (lihat jawaban lain). Anda tidak akan pernah bisa memperbaiki ini pada waktu kompilasi. [ Sunting: Lihat jawaban saya yang lain tentang<const> ]!

Namun sangat mudah untuk mengalami kesalahan yang sama ketika menggunakan konstanta dalam kode Anda yang tidak pernah dimaksudkan untuk menjadi tipe string . Jawaban saya berfokus pada skenario kedua:


Pertama-tama: Mengapa konstanta string 'ajaib' seringkali lebih baik daripada enum?

  • Saya suka cara string konstan terlihat vs enum - itu kompak dan 'javascripty'
  • Lebih masuk akal jika komponen yang Anda gunakan sudah menggunakan konstanta string.
  • Harus mengimpor 'tipe enum' hanya untuk mendapatkan nilai enumerasi bisa menyusahkan
  • Apa pun yang saya lakukan, saya ingin kompilasi aman jadi jika saya menambahkan hapus nilai yang valid dari jenis gabungan, atau salah ketik maka itu HARUS memberikan kesalahan kompilasi.

Untungnya saat Anda mendefinisikan:

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

... Anda benar-benar mendefinisikan penyatuan jenis mana 'missing'sebenarnya merupakan tipe!

Saya sering mengalami kesalahan 'tidak dapat ditugaskan' jika saya memiliki string seperti 'banana'dalam naskah saya dan berpikir kompiler saya maksudkan sebagai string, sedangkan saya benar-benar ingin itu menjadi tipe banana. Seberapa pintar kompiler dapat bergantung pada struktur kode Anda.

Berikut adalah contoh ketika saya mendapat kesalahan ini hari ini:

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

Segera setelah saya menemukan itu 'invalid'atau 'banana'bisa berupa tipe atau string, saya menyadari bahwa saya hanya bisa menegaskan string ke tipe itu . Pada dasarnya melemparkannya ke dirinya sendiri , dan katakan kompiler tidak, saya tidak ingin ini menjadi string !

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

Jadi apa yang salah dengan 'casting' untuk FieldErrorType(atau Fruit)

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

Ini bukan waktu kompilasi yang aman:

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

Mengapa? Ini adalah naskah jadi <FieldErrorType>adalah pernyataan dan Anda memberi tahu kompiler bahwa anjing adalah FieldErrorType ! Dan kompiler akan mengizinkannya!

TETAPI jika Anda melakukan hal berikut, maka kompiler akan mengubah string menjadi tipe

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

Berhati-hatilah terhadap kesalahan ketik bodoh seperti ini:

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

Cara lain untuk memecahkan masalah adalah dengan melemparkan objek induk:

Definisi saya adalah sebagai berikut:

jenis ekspor FieldName = 'angka' | | 'Tanggal kadaluwarsa' | 'cvv'; jenis ekspor FieldError = 'tidak ada' | 'hilang' | 'tidak valid'; jenis ekspor FieldErrorType = {bidang: FieldName, kesalahan: FieldError};

Katakanlah kita mendapatkan kesalahan dengan ini (string tidak dapat ditugaskan kesalahan):

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

Kita dapat 'menegaskan' seluruh objek sebagai FieldErrorTypeberikut:

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

Maka kita menghindari keharusan melakukannya <'invalid'> 'invalid' .

Tapi bagaimana dengan kesalahan ketik? Tidak <FieldErrorType>hanya menegaskan apa pun yang berhak menjadi tipe itu. Tidak dalam kasus ini - untungnya kompiler AKAN mengeluh jika Anda melakukan ini, karena cukup pintar untuk mengetahui bahwa itu tidak mungkin:

  fieldErrors: [ <FieldErrorType> { field: 'number', error: 'dog' } ]
Simon_Weaver
sumber
Mungkin ada kehalusan dengan mode ketat. Akan memperbarui jawaban setelah mengonfirmasi.
Simon_Weaver
1

Semua jawaban di atas valid, namun, ada beberapa kasus bahwa Tipe Literal String adalah bagian dari tipe kompleks lainnya. Perhatikan contoh berikut:

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

Anda memiliki beberapa solusi untuk memperbaikinya. Setiap solusi valid dan memiliki kasus penggunaannya sendiri.

1) Solusi pertama adalah menentukan tipe untuk ukuran dan mengekspornya dari foo.ts. Ini bagus jika ketika Anda perlu bekerja dengan parameter ukuran sendiri. Misalnya, Anda memiliki fungsi yang menerima atau mengembalikan parameter ukuran tipe dan Anda ingin mengetiknya.

  // 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) Pilihan kedua adalah dengan hanya membuangnya ke tipe ToolbarTheme. Dalam hal ini, Anda tidak perlu mengekspos internal ToolbarTheme jika Anda tidak perlu.

  // 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)
  ));
Saman Shafigh
sumber
0

Jika Anda casting ke a dropdownvalue[]Misalnya, data yang mengejek, buatlah itu sebagai array objek dengan nilai dan properti tampilan.

contoh :

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

Saya menghadapi masalah yang sama, saya membuat perubahan di bawah ini dan masalah ini terselesaikan.

Buka file watchQueryOptions.d.ts

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

Ubah tipe kueri apa pun alih-alih DocumentNode , Sama untuk mutasi

Sebelum:

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

Setelah:

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