Call constructor on TypeScript class without new

后端 未结 6 946
醉话见心
醉话见心 2020-12-13 13:50

In JavaScript, I can define a constructor function which can be called with or without new:

function MyCl         


        
6条回答
  •  隐瞒了意图╮
    2020-12-13 14:12

    Here's how I've solved this in jest for testing groups of immutable models. The makeHash function isn't doing anything special, just a utility creating short, random strings from uuid().

    The 'magic' for me was declaring type as new (...args: any[]) => any allowing it to be 'newed' as let model = new set.type(...Object.values(set.args));. So, less about getting around new and more about working in 'newable' forms.

    // models/oauth.ts
    export class OAuthEntity {
      constructor(public readonly id: T) {}
      [key: string]: any;
    }
    
    export class OAuthClient extends OAuthEntity {
      /**
       * An OAuth Client
       * @param id A unique string identifying the client.
       * @param redirectUris Redirect URIs allowed for the client. Required for the authorization_code grant.
       * @param grants Grant types allowed for the client.
       * @param accessTokenLifetime Client-specific lifetime of generated access tokens in seconds.
       * @param refreshTokenLifetime Client-specific lifetime of generated refresh tokens in seconds
       * @param userId The user ID for client credential grants
       */
      constructor(
        public readonly id: string = '',
        public readonly redirectUris: string[] = [],
        public readonly grants: string[] = [],
        public readonly accessTokenLifetime: number = 0,
        public readonly refreshTokenLifetime: number = 0,
        public readonly userId?: string,
        public readonly privateKey?: string
      ) {
        super(id);
      }
    }
    
    // models/oauth.test.ts
    import { makeHash, makePin } from '@vespucci/utils';
    import { OAuthEntity, OAuthClient } from '@vespucci/admin/server/models/oauth';
    
    type ModelData = { type: new (...args: any[]) => any; args: { [key: string]: any }; defs?: { [key: string]: any } };
    
    describe('Model Tests', () => {
      const dataSet: ModelData[] = [
        { type: OAuthEntity, args: { id: makeHash() } },
        {
          type: OAuthClient,
          args: {
            id: makeHash(),
            redirectUris: [makeHash()],
            grants: [makeHash()],
            accessTokenLifetime: makePin(2),
            refreshTokenLifetime: makePin(2),
            userId: makeHash(),
            privateKey: makeHash(),
          },
        },
        {
          type: OAuthClient,
          args: {},
          defs: {
            id: '',
            redirectUris: [],
            grants: [],
            accessTokenLifetime: 0,
            refreshTokenLifetime: 0,
          },
        },
      ];
      dataSet.forEach((set) => {
        it(`Creates ${set.type.name} With ${Object.keys(set.args).length} Args As Expected`, () => {
          let model!: any;
          const checkKeys = Object.keys(set.args).concat(Object.keys(set.defs || {}).filter((k) => !(k in set.args)));
          const checkValues: any = checkKeys
            .map((key) => ({ [key]: set.args[key] || set.defs?.[key] }))
            .reduce((p, c) => ({ ...p, ...c }), {});
          expect(() => {
            model = new set.type(...Object.values(set.args));
          }).not.toThrow();
          expect(model).toBeDefined();
          checkKeys.forEach((key) => expect(model[key]).toEqual(checkValues[key]));
        });
      });
    });
    

    The end result, for me, is:

提交回复
热议问题