Bir Typcript nesnesinin dizinlenmiş üyelerinin türü zorunlu mu?


290

Ben bir dizesi -> dizesi bir Typescript nesnesinde depolamak ve tüm anahtarları dizeleri eşlemek için zorlamak istiyorum. Örneğin:

var stuff = {};
stuff["a"] = "foo";   // okay
stuff["b"] = "bar";   // okay
stuff["c"] = false;   // ERROR!  bool != string

Değerlerin dizeler (ya da herhangi bir tür ..) olması gerektiğini zorlamanın bir yolu var mı?

Yanıtlar:


521
var stuff: { [key: string]: string; } = {};
stuff['a'] = ''; // ok
stuff['a'] = 4;  // error

// ... or, if you're using this a lot and don't want to type so much ...
interface StringMap { [key: string]: string; }
var stuff2: StringMap = { };
// same as above

13
Bu sözdiziminde ilginç olan şey, anahtar için 'dize' dışında herhangi bir türün aslında yanlış olmasıdır. JS haritalarının dizeler tarafından açıkça kodlandığı göz önüne alındığında mükemmel bir anlam ifade eder, ancak sözdizimini biraz gereksiz hale getirir. Üretilen kodun bir parçası olarak anahtarların otomatik olarak zorlanmasına izin verecek şekilde eklenmedikçe, değer türlerini belirtmek için '{}: string' gibi bir şey daha basit görünecektir.
Ermenistan

43
numberindeksleme türü olarak da kullanılabilir
Ryan Cavanaugh

3
kayda değer: derleyici anahtar türünü değil, yalnızca değer türünü zorlar. bir şeyler yapabilirsiniz [15] = 'her neyse' ve şikayet etmeyecektir.
amwinter

3
Hayır, anahtar türünü zorunlu kılar. MyObject uygulamasının güzel bir toString () uygulaması olsa bile [myObject] = 'her neyse' şeyler yapamazsınız.
AlexG

7
@RyanCavanaugh type Keys = 'Acceptable' | 'String' | 'Keys'Bir dizin oluşturma (anahtar) türü olarak kullanmak mümkün mü (ya da olacak mı)?
calebboyd

131
interface AgeMap {
    [name: string]: number
}

const friendsAges: AgeMap = {
    "Sandy": 34,
    "Joe": 28,
    "Sarah": 30,
    "Michelle": "fifty", // ERROR! Type 'string' is not assignable to type 'number'.
};

Burada, arabirim AgeMapanahtarları dize, değerleri sayı olarak zorlar. Anahtar kelime nameherhangi bir tanımlayıcı olabilir ve arabiriminizin / türünüzün sözdizimini önermek için kullanılmalıdır.

Bir nesnenin birleşim türündeki her giriş için bir anahtarı olduğunu zorlamak için benzer bir sözdizimi kullanabilirsiniz:

type DayOfTheWeek = "sunday" | "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday";

type ChoresMap = { [day in DayOfTheWeek]: string };

const chores: ChoresMap = { // ERROR! Property 'saturday' is missing in type '...'
    "sunday": "do the dishes",
    "monday": "walk the dog",
    "tuesday": "water the plants",
    "wednesday": "take out the trash",
    "thursday": "clean your room",
    "friday": "mow the lawn",
};

Elbette, bunu genel bir tür haline getirebilirsiniz!

type DayOfTheWeek = "sunday" | "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday";

type DayOfTheWeekMap<T> = { [day in DayOfTheWeek]: T };

const chores: DayOfTheWeekMap<string> = {
    "sunday": "do the dishes",
    "monday": "walk the dog",
    "tuesday": "water the plants",
    "wednesday": "take out the trash",
    "thursday": "clean your room",
    "friday": "mow the lawn",
    "saturday": "relax",
};

const workDays: DayOfTheWeekMap<boolean> = {
    "sunday": false,
    "monday": true,
    "tuesday": true,
    "wednesday": true,
    "thursday": true,
    "friday": true,
    "saturday": false,
};

10.10.2018 güncelleme: @ dracstaxi'nin cevabını aşağıda görebilirsiniz - şimdi Recordsizin için bunu yapan yerleşik bir tür var.

1.2.2020 güncellemesi: Cevabımdan önceden yapılmış haritalama arayüzlerini tamamen kaldırdım. @ dracstaxi'nin cevabı onları tamamen ilgisiz kılar. Bunları kullanmaya devam etmek istiyorsanız düzenleme geçmişini kontrol edin.


1
{[anahtar: sayı]: T; } typesafe değildir çünkü bir nesnenin anahtarları her zaman bir dizedir - daha fazla ayrıntı için @tep tarafından soruya bakınız. Örneğin x = {}; x[1] = 2;, Chrome'da Object.keys(x)yayınlamak ["1"] JSON.stringify(x)döndürür ve '{"1": 2}' döndürür. NumberTypeof (örn. Infinity, NaN, 1e300, 999999999999999999999 vb.) Olan köşe kutuları dize anahtarlarına dönüştürülür. Ayrıca x[''] = 'empty string';, gibi dize tuşları için diğer köşe durumlarda sakının x['000'] = 'threezeros'; x[undefined] = 'foo'.
robocat

@robocat Bu doğrudur ve bir süredir bu cevaptan sayı anahtarlı arayüzleri kaldırmak için düzenlemeye devam ettim. Sonuçta, TypeScript'i teknik olarak ve özellikle anahtar olarak sayılara izin verdiğinden onları tutmaya karar verdim . Bunu söyledikten sonra, umarım sayılarla endekslenmiş nesneleri kullanmaya karar veren herkes yorumunuzu görür.
Sandy Gifford

76

Hızlı güncelleme: Typescript 2.1'den beri Record<T, K>sözlük gibi davranan yerleşik bir tür vardır.

Dokümanlardan bir örnek:

// For every properties K of type T, transform it to U
function mapObject<K extends string, T, U>(obj: Record<K, T>, f: (x: T) => U): Record<K, U>

const names = { foo: "hello", bar: "world", baz: "bye" };
const lengths = mapObject(names, s => s.length);  // { foo: number, bar: number, baz: number }

TypeScript 2.1 Belgeleri açık Record<T, K>

Ben en baştan kullanmaya gördüğüm tek dezavantajı {[key: T]: K}size nesne yalnızca asal anahtarları olsaydı mesela bunu gibi bu ipucu olabilir ne tür anahtarın siz "anahtar" yerine kullandığınız hakkında yararlı bilgi kodlamak olmasıdır: {[prime: number]: yourType}.

İşte bu dönüşümlere yardımcı olmak için yazdığım bir regex. Bu yalnızca etiketin "anahtar" olduğu durumları dönüştürür. Diğer etiketleri dönüştürmek için ilk yakalama grubunu değiştirmeniz yeterlidir:

bul: \{\s*\[(key)\s*(+\s*:\s*(\w+)\s*\]\s*:\s*([^\}]+?)\s*;?\s*\}

Değiştir: Record<$2, $3>


3
Bu daha fazla oy almalı - aslında cevabımın daha yeni, yerel versiyonu.
Sandy Gifford

Kayıt bir olarak derleniyor mu {}?
Douglas Gaskell

@DouglasGaskell türlerinin derlenmiş kodda varlığı yoktur. Records (aksine, Javascript Maps) yalnızca bir nesne değişmezinin içeriğini zorlamak için bir yol sağlar. Yapamazsınız new Record...ve const blah: Record<string, string>;derlenir const blah;.
Sandy Gifford

Bu cevabın bana ne kadar yardımcı olduğunu hayal bile edemezsin, çok teşekkür ederim :)
Federico Grandi

Dize sendikalarının Records'de de çalıştığını belirtmeye değer : const isBroken: Record<"hat" | "teapot" | "cup", boolean> = { hat: false, cup: false, teapot: true };
Sandy Gifford

10

@Ryan Cavanaugh'ın cevabı tamamen iyi ve hala geçerli. Yine de, ES6'nın platformların çoğunluğu tarafından desteklendiğini iddia edebildiğimiz Güz16'dan itibaren, bazı verileri bazı anahtarlarla ilişkilendirmeniz gerektiğinde Haritaya bağlı kalmanın neredeyse her zaman daha iyi olduğunu eklemeye değer.

Biz yazarken let a: { [s: string]: string; }biz typescript derlenmiş sonra tip verileri gibi böyle bir şey yok olmadığını unutmamak gerekir, sadece derleme için kullanılıyor. Ve {[s: string]: string; } sadece {} için derlenecek.

Bununla birlikte, şöyle bir şey yazsanız bile:

class TrickyKey  {}

let dict: {[key:TrickyKey]: string} = {}

Bu sadece derlenmeyecek (hatta target es6,error TS1023: An index signature parameter type must be 'string' or 'number'.

Bu yüzden pratikte dize veya sayı ile potansiyel anahtar olarak sınırlısınız, bu nedenle burada tür denetimini zorlama hissi çok fazla değildir, özellikle js anahtara numaraya erişmeye çalıştığında onu dizeye dönüştürdüğünü unutmayın.

Bu nedenle, anahtarlar dize olsa bile en iyi uygulamanın Harita'yı kullanmak olduğunu varsaymak oldukça güvenlidir, bu yüzden:

let staff: Map<string, string> = new Map();

4
Bu yanıt gönderildiğinde bunun mümkün olup olmadığından emin değilim, ancak bugün bunu yapabilirsiniz: let dict: {[key in TrickyKey]: string} = {}- burada TrickyKeybir dize değişmez türü (örn. "Foo" | "Bar").
Roman Starkov

Şahsen ben yerel daktilo biçimini seviyorum ama standart kullanmak için en iyisidir. Bana gerçekten kullanışlı olmayan anahtar "isim" belgelemek için bir yol verir ama "hasta kimliği" gibi bir şey denilen anahtar yapabilirsiniz :)
kod

Bu cevap kesinlikle geçerlidir ve çok iyi puanlar verir, ancak yerel Mapnesnelere bağlı kalmanın neredeyse her zaman daha iyi olduğu fikrine katılmıyorum . Haritalar ek bellek yükü ile birlikte gelir ve (daha da önemlisi) JSON dizesi olarak depolanan tüm verilerden manuel olarak başlatılması gerekir. Genellikle çok faydalıdırlar, ancak sadece onlardan tür almak uğruna değil.
Sandy Gifford

7

Arabirimi tanımla

interface Settings {
  lang: 'en' | 'da';
  welcome: boolean;
}

Anahtarı, Ayarlar arayüzünün belirli bir anahtarı olmaya zorlama

private setSettings(key: keyof Settings, value: any) {
   // Update settings key
}

3

@ Shabunc'un cevabına dayanarak bu, ya anahtarın ya da değerin - ya da her ikisinin - uygulamak istediğiniz herhangi bir şey olmasını sağlamaya izin verecektir.

type IdentifierKeys = 'my.valid.key.1' | 'my.valid.key.2';
type IdentifierValues = 'my.valid.value.1' | 'my.valid.value.2';

let stuff = new Map<IdentifierKeys, IdentifierValues>();

Ayrıca enumbir typetanım yerine kullanarak çalışmalıdır .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.