JSON'dan TypeScript'e

JSON'dan TypeScript arayüzleri üretir: dizi öğeleri birleştirilir, isteğe bağlı anahtarlar, karışık türlerde union, null ayrı — tümü tarayıcınızda.

Girdi
TypeScript
export interface Root {
  id: number
  name: string
  active: boolean
  address: Address
  roles: string[]
  posts: Post[]
  tags: unknown[]
}

export interface Address {
  city: string
  zip: null
}

export interface Post {
  id: number
  title: string
  views: number | string
  pinned?: boolean
}

Arayüzler: 3

Türler verilerden çıkarılır, tahmin edilmez

Bir JSON belgesi kendi türlerini taşımaz — değerler taşır, ve türlerin bunlardan geri okunması gerekir. Bu, tek bir nesne için kolaydır ve bir koleksiyon için şaşırtıcı derecede inceliklidir: istediğiniz biçim herhangi bir tek kaydın biçimi değil, her kaydın karşılaması gereken biçimdir. Bu araç, örneğin içerdiği her şeye bakarak bir JSON örneğinden TypeScript arayüzleri çıkarır; böylece ürettiği türler, tepede duran ilk satırı değil verilerinizin tümünü tanımlar.

Dönüşüm yalnızca tek yönlüdür. Türleri JSON'a geri çevirmek değerler uydurmak anlamına gelir; buradaki amaç ise tersidir: yapıştırılan belge doğruluğun kaynağıdır ve her bildirim ondan türetilir. Bir API yanıtı, bir yapılandırma dosyası veya bir log satırı yapıştırın ve aksi halde elle yazacağınız arayüzü okuyun.

Diziler nasıl birleştirilir

İlginç kararların hepsi dizilerde gerçekleşir. Naif bir dönüştürücü ilk öğeye bakıp durur; bu, tesadüfen gördüğü her alanı zorunlu gösterir ve görmediği her alanı kaçırır. Bu araç ise tüm öğeleri tek bir türde birleştirir ve bundan üç şey çıkar:

  • Bazı öğelerde bulunan ama diğerlerinde bulunmayan bir anahtar isteğe bağlı olur ve bir soru işaretiyle yazılır. Kayıtlarınızın yarısında "middleName" varsa ve yarısında yoksa, alan "middleName?" olur; bir tüketicinin ele alması gereken tam da budur.
  • Değeri öğeler arasında türce farklılaşan bir anahtar bir union olur. Bir kayıtta sayı, başka bir kayıtta metin olan bir alan "number | string" olarak türlenir — düzen için değil, verilerin gerçekten içerdiği ve kodunuzun kabul etmesi gereken şey bu olduğu için.
  • Öğelerin içindeki iç içe bir nesne aynı şekilde, özyinelemeli olarak birleştirilir ve kendi arayüzüne çıkarılır. Her biri bir "address" taşıyan on dizi öğesi, onunun hepsini tanımlayan tek bir Address arayüzü üretir.

Belgenin kendisi en üst düzeyde bir diziyse, kök bir öğe arayüzüne takma ad olur — örneğin "type Root = RootItem[]" — ve birleştirilmiş öğe türü altına yazılır.

Null, isteğe bağlı ve neden farklılar

Bir null'ı eksik bir anahtarla aynı saymak caziptir, ama yanlıştır. TypeScript'te "name?: string" özelliğin bulunmayabileceği anlamına gelir; "name: string | null" ise her zaman var olduğu ama null tutabileceği anlamına gelir. Bunlar farklı sözleşmelerdir ve bir tüketici onları farklı denetler — "in" ile bir değer karşılaştırması. Bu araç ikisini ayrı tutar: verilerdeki açık bir null, "string | null" beklediğiniz gibi okunsun diye en sona konan bir union üyesi "| null" olur; bazı kayıtlarda yalnızca bulunmayan bir anahtar ise isteğe bağlı olur. Hem o hem bu olan bir alan — bir kayıtta null, başka bir kayıtta eksik — ikisi birden, "field?: T | null" olarak çıkar, çünkü her iki olgu da verileriniz için doğrudur.

İç içe nesneler adlandırılmış arayüzlere dönüşür

İç içe bir biçimi ebeveynine gömmek yerine, her nesne, adı altında bulunduğu anahtardan türeyen kendi arayüzüne çıkarılır. Bir "user" nesnesi bir User arayüzü olur; içindeki bir "address" ise User'ın başvurduğu bir Address arayüzü olur. Derinlemesine gömülü türler okunması zor ve yeniden kullanılması imkânsızdır; adlandırılmış arayüzler ise kendinizin yazacağı şeydir. Dizi öğeleri mümkün olduğunda tekil yapılır — "users" bir User, "categories" bir Category verir — ve çoğullaşmayan bir anahtar, öğenin kendi adı olsun diye Item ekini alır.

İki farklı nesne aynı adı alacak olsaydı — diyelim ki ilgisiz iki "data" — ikincisi birleştirilmek yerine bir ek alır; böylece ayrı biçimler ayrı kalır. Kök nesne ilk çıkarılır ve adını değiştirebilirsiniz; "interface" ile "type" çıktısı arasındaki seçim bir anahtardır, çünkü bazı kod tabanları baştan sona tür takma adlarını yeğler.

unknown'a geri düşme

Bazı değerler hiçbir tür bilgisi taşımaz. Boş bir dizi her şeyi tutabilir; boş bir nesnenin tanımlanacak anahtarı yoktur. Sonraki her şey için tür denetimini kapatan "any"e başvurmak yerine, araç "unknown"a geri düşer: boş bir dizi "unknown[]" olur, boş bir nesne "Record<string, unknown>" olur. Fark önemlidir. "any" hataları sessizce geçirir; "unknown" ise tüketiciyi kullanmadan önce değeri daraltmaya zorlar, böylece çıkarılan tür örneğin söylediği ve söylemediği konusunda dürüst kalır.

Bunlar, boş koleksiyonun ne tutması gerektiğini öğrenir öğrenmez elle sıkılaştıracağınız türlerdir — ama veriler bunu söyleyene kadar "unknown" dürüst yanıttır ve türlerinizin geri kalanını güvende tutan yanıttır.

Neyin üzerinde ve nerede çalışır

Her şey tarayıcınızda olur. JSON ayrıştırılır ve türler kendi cihazınızda çıkarılır; yapıştırdığınız hiçbir şey yüklenmez, saklanmaz veya kaydedilmez. Bu, aracı gerçek bir API yanıtında veya içinde sırlar olan bir yapılandırma dosyasında kullanmak için güvenli kılar: örnek sayfayı asla terk etmez. Çıktı, doğrudan bir "d.ts" dosyasına veya bir modüle yapıştırabileceğiniz, verilerin tanımlayamadığı birkaç "unknown" alanı ayarlayıp kullanabileceğiniz sıradan TypeScript'tir.

Sıkça sorulan sorular

JSON'um bir sunucuya gönderiliyor mu?
Hayır. Belge ayrıştırılır ve türler tamamen tarayıcınızda çıkarılır, ve yapıştırdığınız hiçbir şey yüklenmez veya kaydedilmez. Gerçek bir API yanıtında veya yapılandırma dosyasında kullanmak güvenlidir.
Örneğimde varken bir alan neden isteğe bağlı oluyor?
Çünkü aracın birleştirdiği bir dizinin en az bir öğesinde yok. Çıkarım yalnızca ilkini değil her öğeyi okur, bu yüzden bazı kayıtların atladığı bir anahtar isteğe bağlı olur — baktığınız kayıt onu tesadüfen içerse bile, verilerinizin gerçekten desteklediği tür budur.
Bir alan neden string | number gibi bir union oldu?
Çünkü değer dizinin farklı öğelerinde farklı türlerdeydi — bir kayıtta metin, başka bir kayıtta sayı. Birleştirilmiş tür ikisini de kabul etmelidir, bu yüzden bir union olarak yazılır. Bu sizi şaşırtıyorsa, genellikle verilerin beklenenden daha az tekdüze olduğu anlamına gelir, ki bunu bilmek yararlıdır.
Araç neden any yerine unknown kullanıyor?
Tanımlayamadığı değerler için — boş bir dizi, boş bir nesne — "unknown" sonucu tür açısından güvenli tutar ve bir tüketiciyi kullanmadan önce değeri daraltmaya zorlarken, "any" tür denetimini kapatırdı. Boş koleksiyonun ne tuttuğunu öğrenir öğrenmez bu alanları elle sıkılaştırabilirsiniz.
interface ve type çıktısı arasındaki fark nedir?
Tanımladıkları türlerde hiçbiri — ikisi de aynı biçimleri üretir. "interface", nesne türleri için yaygın deyimdir ve genişletilebilir ve birleştirilebilir; "type" takma adları ise bazı kod tabanlarının baştan sona kullanmayı yeğlediği şeydir. Anahtar, çıktının projenizin stiline uyması için vardır.
TypeScript'i tekrar JSON'a çevirebilir mi?
Hayır, ve bilerek. Dönüşüm tek yönlüdür: JSON girer, türler çıkar. Diğer yöne gitmek verilerinizde hiç olmamış değerler uydurmak anlamına gelir; bütün mesele her bildirimin gerçekten yapıştırdığınız şeyden türetilmesidir.
İç içe nesneler nasıl adlandırılır?
Altında bulundukları anahtardan: bir "user" nesnesi User olur, içindeki bir "address" Address olur. Dizi öğeleri mümkün olduğunda tekil yapılır — "categories" Category verir — ve çoğullaşmayan bir anahtar Item ekini alır. Bir adda çakışacak iki farklı biçim, birleştirilmek yerine bir ek alır, böylece ayrı kalır.