Debido a que el tipo es desconocido en tiempo de ejecución, escribí el siguiente código para comparar el objeto desconocido, no contra un tipo, sino contra un objeto de tipo conocido:
- Crear un objeto de muestra del tipo correcto
- Especifica cuáles de sus elementos son opcionales
- Haga una comparación profunda de su objeto desconocido con este objeto de muestra
Aquí está el código (interfaz agnóstico) que uso para la comparación profunda:
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;
}
A continuación se muestra un ejemplo de cómo lo uso.
En este ejemplo, espero que el JSON contenga una matriz de tuplas, de las cuales el segundo elemento es una instancia de una interfaz llamada User
(que tiene dos elementos opcionales).
La verificación de tipos de TypeScript asegurará que mi objeto de muestra sea correcto, luego la función afirmarTypeT verifica que el objeto desconocido (cargado desde JSON) coincida con el objeto de muestra.
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);
}
Podría invocar una verificación como esta en la implementación de una protección de tipo definida por el usuario.