Parce que le type est inconnu au moment de l'exécution, j'ai écrit le code comme suit pour comparer l'objet inconnu, non pas contre un type, mais contre un objet de type connu:
- Créer un exemple d'objet du bon type
- Spécifiez lesquels de ses éléments sont facultatifs
- Faites une comparaison approfondie de votre objet inconnu avec cet exemple d'objet
Voici le code (interface-agnostique) que j'utilise pour la comparaison approfondie:
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;
}
Voici un exemple de la façon dont je l'utilise.
Dans cet exemple, je m'attends à ce que le JSON contienne un tableau de tuples, dont le deuxième élément est une instance d'une interface appelée User
(qui a deux éléments facultatifs).
La vérification de type de TypeScript garantira que mon exemple d'objet est correct, puis la fonction assertTypeT vérifie que l'objet inconnu (chargé à partir de JSON) correspond à l'exemple d'objet.
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);
}
Vous pouvez invoquer une vérification comme celle-ci dans l'implémentation d'un type de garde défini par l'utilisateur.