Interface type check with Typescript

前端 未结 17 1018
灰色年华
灰色年华 2020-11-22 14:09

This question is the direct analogon to Class type check with TypeScript

I need to find out at runtime if a variable of type any implements an interface. Here\'s my

17条回答
  •  心在旅途
    2020-11-22 14:23

    Because the type is unknown at run-time, I wrote code as follows to compare the unknown object, not against a type, but against an object of known type:

    1. Create a sample object of the right type
    2. Specify which of its elements are optional
    3. Do a deep compare of your unknown object against this sample object

    Here's the (interface-agnostic) code I use for the deep compare:

    function assertTypeT(loaded: any, wanted: T, optional?: Set): 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;
    }
    
    

    Below is an example of how I use it.

    In this example I expect the JSON contains an array of tuples, of which the second element is an instance of an interface called User (which has two optional elements).

    TypeScript's type-checking will ensure that my sample object is correct, then the assertTypeT function checks that the unknown (loaded from JSON) object matches the sample object.

    export function loadUsers(): Map {
      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 = new Set(["profile.aboutMe", "profile.location"]);
      const loaded: [number, User][] = assertTypeT(found, [sample], optional);
      return new Map(loaded);
    }
    
    

    You could invoke a check like this in the implementation of a user-defined type guard.

提交回复
热议问题