Da der Typ zur Laufzeit unbekannt ist, habe ich folgenden Code geschrieben, um das unbekannte Objekt nicht mit einem Typ, sondern mit einem Objekt bekannten Typs zu vergleichen:
- Erstellen Sie ein Beispielobjekt des richtigen Typs
- Geben Sie an, welche Elemente optional sind
- Vergleichen Sie Ihr unbekanntes Objekt gründlich mit diesem Beispielobjekt
Hier ist der (schnittstellenunabhängige) Code, den ich für den Tiefenvergleich verwende:
function assertTypeT<T>(loaded: any, wanted: T, optional?: Set<string>): T {
// this is called recursively to compare each element
function assertType(found: any, wanted: any, keyNames?: string): void {
if (typeof wanted !== typeof found) {
throw new Error(`assertType expected ${typeof wanted} but found ${typeof found}`);
}
switch (typeof wanted) {
case "boolean":
case "number":
case "string":
return; // primitive value type -- done checking
case "object":
break; // more to check
case "undefined":
case "symbol":
case "function":
default:
throw new Error(`assertType does not support ${typeof wanted}`);
}
if (Array.isArray(wanted)) {
if (!Array.isArray(found)) {
throw new Error(`assertType expected an array but found ${found}`);
}
if (wanted.length === 1) {
// assume we want a homogenous array with all elements the same type
for (const element of found) {
assertType(element, wanted[0]);
}
} else {
// assume we want a tuple
if (found.length !== wanted.length) {
throw new Error(
`assertType expected tuple length ${wanted.length} found ${found.length}`);
}
for (let i = 0; i < wanted.length; ++i) {
assertType(found[i], wanted[i]);
}
}
return;
}
for (const key in wanted) {
const expectedKey = keyNames ? keyNames + "." + key : key;
if (typeof found[key] === 'undefined') {
if (!optional || !optional.has(expectedKey)) {
throw new Error(`assertType expected key ${expectedKey}`);
}
} else {
assertType(found[key], wanted[key], expectedKey);
}
}
}
assertType(loaded, wanted);
return loaded as T;
}
Unten ist ein Beispiel, wie ich es benutze.
In diesem Beispiel erwarte ich, dass JSON ein Array von Tupeln enthält, von denen das zweite Element eine Instanz einer aufgerufenen Schnittstelle ist User
(die zwei optionale Elemente enthält).
Durch die Typprüfung von TypeScript wird sichergestellt, dass mein Beispielobjekt korrekt ist. Anschließend überprüft die Funktion assertTypeT, ob das unbekannte (von JSON geladene) Objekt mit dem Beispielobjekt übereinstimmt.
export function loadUsers(): Map<number, User> {
const found = require("./users.json");
const sample: [number, User] = [
49942,
{
"name": "ChrisW",
"email": "example@example.com",
"gravatarHash": "75bfdecf63c3495489123fe9c0b833e1",
"profile": {
"location": "Normandy",
"aboutMe": "I wrote this!\n\nFurther details are to be supplied ..."
},
"favourites": []
}
];
const optional: Set<string> = new Set<string>(["profile.aboutMe", "profile.location"]);
const loaded: [number, User][] = assertTypeT(found, [sample], optional);
return new Map<number, User>(loaded);
}
Sie können eine solche Prüfung bei der Implementierung eines benutzerdefinierten Typschutzes aufrufen.