Chciałbym zapisać mapowanie ciągu -> ciąg w obiekcie Typescript i wymusić, aby wszystkie klucze były odwzorowane na ciągi. Na przykład:
var stuff = {};
stuff["a"] = "foo"; // okay
stuff["b"] = "bar"; // okay
stuff["c"] = false; // ERROR! bool != string
Czy istnieje sposób na wymuszenie, że wartości muszą być łańcuchami (lub jakimkolwiek typem ...)?
typescript
Armentaż
źródło
źródło
number
jest również dozwolony jako typ indeksowaniatype Keys = 'Acceptable' | 'String' | 'Keys'
jako typu indeksowania (klucza)?Interfejs
AgeMap
wymusza klucze jako ciągi, a wartości jako liczby. Słowo kluczowename
może być dowolnym identyfikatorem i powinno być używane do sugerowania składni interfejsu / typu.Możesz użyć podobnej składni, aby wymusić, że obiekt ma klucz dla każdej pozycji w typie unii:
Możesz oczywiście uczynić to także typem ogólnym!
Aktualizacja 10.10.2018: Sprawdź odpowiedź @ dracstaxi poniżej - teraz jest wbudowany typ,
Record
który robi to za Ciebie.Aktualizacja 1.2.2020: Całkowicie usunąłem gotowe interfejsy mapowania z mojej odpowiedzi. Odpowiedź @ dracstaxi czyni je całkowicie nieistotnymi. Jeśli nadal chcesz ich używać, sprawdź historię edycji.
źródło
x = {}; x[1] = 2;
w ChromeObject.keys(x)
zwraca następnie [„1”] iJSON.stringify(x)
zwraca „{„ 1 ”: 2}”.Number
Skrzynie narożne z typof (np. Infinity, NaN, 1e300, 99999999999999999999999 itp.) Są konwertowane na klucze łańcuchowe. Uważaj też na pozostałych przypadkach narożnych dla kluczy strunowych, takich jakx[''] = 'empty string';
,x['000'] = 'threezeros';
x[undefined] = 'foo'
.Szybka aktualizacja: od Typescript 2.1 istnieje wbudowany typ,
Record<T, K>
który działa jak słownik.Przykład z dokumentów:
Dokumentacja TypeScript 2.1 włączona
Record<T, K>
Jedyną wadą widzę użyciem nad tym
{[key: T]: K}
jest to, że można zakodować przydatnych informacji na temat, jaki rodzaj klucza używasz zamiast „klucza” np jeśli obiekt miał tylko klawisze prime można wskazywać na to tak:{[prime: number]: yourType}
.Oto regex, który napisałem, aby pomóc w tych konwersjach. Spowoduje to konwersję tylko przypadków, w których etykieta jest „kluczowa”. Aby przekonwertować inne etykiety, wystarczy zmienić pierwszą grupę przechwytywania:
Odnaleźć:
\{\s*\[(key)\s*(+\s*:\s*(\w+)\s*\]\s*:\s*([^\}]+?)\s*;?\s*\}
Zastąpić:
Record<$2, $3>
źródło
{}
?Record
s (w przeciwieństwie do, powiedzmy, JavascriptMap
), umożliwiają jedynie wymuszenie zawartości literału obiektu. Nie możesznew Record...
iconst blah: Record<string, string>;
będziesz się kompilowaćconst blah;
.Record
również w:const isBroken: Record<"hat" | "teapot" | "cup", boolean> = { hat: false, cup: false, teapot: true };
Odpowiedź Ryana Cavanaugha jest całkowicie poprawna i nadal aktualna. Warto jednak dodać, że od jesieni'16, kiedy możemy twierdzić, że ES6 jest obsługiwany przez większość platform, prawie zawsze lepiej jest trzymać się mapy, gdy potrzebujesz powiązać niektóre dane z jakimś kluczem.
Kiedy piszemy
let a: { [s: string]: string; }
, musimy pamiętać, że po skompilowaniu maszynopisu nie ma czegoś takiego jak typ danych, jest on używany tylko do kompilacji. I {[s: string]: string; } skompiluje się do {}.To powiedziawszy, nawet jeśli napiszesz coś takiego:
To po prostu się nie skompiluje (nawet dla
target es6
, dostanieszerror TS1023: An index signature parameter type must be 'string' or 'number'.
Tak więc praktycznie jesteś ograniczony ciągiem lub liczbą jako potencjalnym kluczem, więc nie ma tutaj większego sensu wymuszania sprawdzania typu, szczególnie biorąc pod uwagę, że gdy js próbuje uzyskać dostęp do klucza po numerze, konwertuje go na ciąg.
Można więc bezpiecznie założyć, że najlepszą praktyką jest używanie mapy, nawet jeśli klucze są ciągami, więc trzymałbym się:
źródło
let dict: {[key in TrickyKey]: string} = {}
- gdzieTrickyKey
jest literał ciąg typu (np"Foo" | "Bar"
.).Map
obiektów. Mapy mają dodatkowy narzut pamięci i (co ważniejsze) muszą być tworzone ręcznie z dowolnych danych przechowywanych jako ciąg JSON. Często są bardzo przydatne, ale nie tylko w celu wydobycia z nich typów.Zdefiniuj interfejs
Wymuś klucz, aby był określonym kluczem interfejsu ustawień
źródło
Opierając się na odpowiedzi @ shabunc, pozwoliłoby to na wymuszenie klucza lub wartości - lub obu - na wszystko, co chcesz wymusić.
Powinien również działać przy użyciu
enum
zamiasttype
definicji.źródło