账号密码登录
微信安全登录
微信扫描二维码登录

登录后绑定QQ、微信即可实现信息互通

手机验证码登录
找回密码返回
邮箱找回 手机找回
注册账号返回
其他登录方式
分享
  • 收藏
    X
    TypeScript Type 'xxx' cannot be used as an index type
    • 2020-01-01 00:00
    • 11
    33
    0

    Linux运行ts-node报错
    error TS2538: Type 'ITransformConfigObject' cannot be used as an index type.

    89 transformConfig[field] = true;

    import * as lodash from 'lodash';
    import { Types } from 'mongoose';
    import { createParamDecorator } from '@nestjs/common';
    import { HttpForbiddenError } from '@app/errors/forbidden.error';
    import { HttpBadRequestError } from '@app/errors/bad-request.error';
    import { EPublishState, EPublicState, EOriginState, ECommentState, ESortType } from '@app/interfaces/state.interface';
    
    // 预置转换器可选字段
    export enum EQueryParamsField {
      Page = 'page',
      PerPage = 'per_page',
      Sort = 'sort',
      Date = 'date',
      Keyword = 'keyword',
      State = 'state',
      Public = 'public',
      Origin = 'origin',
      ParamsId = 'paramsId',
      CommentState = 'commentState',
    }
    
    // 内部参数类型
    export interface IQueryParamsConfig {
      [key: string]: string | number | boolean | Types.ObjectId | Date | RegExp | IQueryParamsConfig;
    }
    
    // 导出结构
    export interface IQueryParamsResult {
      querys: IQueryParamsConfig; // 用于 paginate 的查询参数
      options: IQueryParamsConfig; // 用于 paginate 的查询配置参数
      params: IQueryParamsConfig; // 路由参数
      origin: IQueryParamsConfig; // 原味的 querys 参数
      request: any; // 用于 request 的对象
      visitors: { // 访客信息
        ip: string; // 真实 IP
        ua: string; // 用户 UA
        referer: string; // 跳转来源
      };
      isAuthenticated: boolean; // 是否鉴权
    }
    
    // 入参转换配置
    interface ITransformConfigObject {
      [key: string]: string | number | boolean;
    }
    export type TTransformConfig = EQueryParamsField | string | ITransformConfigObject;
    
    // 验证器结构
    interface IValidateError {
      name: string;
      field: EQueryParamsField;
      isAllowed: boolean;
      isIllegal: boolean;
      setValue(): void;
    }
    
    /**
     * 参数解析器构造器
     * @function QueryParams
     * @description 根据入参配置是否启用某些参数的验证和解析
     * @example @QueryParams()
     * @example @QueryParams([EQPFields.State, EQPFields.Date, { [EQPFields.Page]: 1 }])
     * @example @QueryParams(['custom_query_params', { test_params: true, [EQueryParamsField.Sort]: false }])
     */
    export const QueryParams = createParamDecorator((customConfig: TTransformConfig[], request: any): IQueryParamsResult => {
    
      // 是否已验证权限
      const isAuthenticated = request.isAuthenticated();
    
      // 字段转换配置(字符串则代表启用,对象则代表默认值)
      const transformConfig: IQueryParamsConfig = {
        [EQueryParamsField.Page]: 1,
        [EQueryParamsField.PerPage]: true,
        [EQueryParamsField.ParamsId]: 'id',
        [EQueryParamsField.Sort]: true,
      };
    
      // 合并配置
      if (customConfig) {
        customConfig.forEach(field => {
          if (lodash.isString(field)) {
            transformConfig[field] = true;
          }
          if (lodash.isObject(field)) {
            Object.assign(transformConfig, field);
          }
        });
      }
    
      console.log('--------------------------------- transformConfig\n', transformConfig);
    
      // 查询参数
      const querys: IQueryParamsConfig = {};
    
      // 过滤条件
      const options: IQueryParamsConfig = {};
    
      // 路径参数
      const params: IQueryParamsConfig = lodash.merge({ url: request.url }, request.params);
    
      // 初始参数
      const date = request.query.date;
      const paramsId = request.params[transformConfig.paramsId as string];
      const [page, per_page, sort, state, ppublic, origin] = [
        request.query.page || transformConfig.page,
        request.query.per_page,
        request.query.sort,
        request.query.state,
        request.query.public,
        request.query.origin,
      ].map(item => item != null ? Number(item) : item);
    
      // 参数提取验证规则
      // 1. field 用于校验这个字段是否被允许用做参数
      // 2. isAllowed 请求参数是否在允许规则之内 -> 400
      // 3. isIllegal 请求参数是否不合法地调用了管理员权限参数 -> 403
      // 任一条件返回错误;否则,设置或重置参数
      const validates: IValidateError[] = [
        {
          name: '路由/ID',
          field: EQueryParamsField.ParamsId,
          isAllowed: true,
          isIllegal: paramsId != null && !isAuthenticated && isNaN(paramsId),
          setValue() {
            // 如果用户传了 ID,则转为数字或 ObjectId
            if (paramsId != null) {
              params[transformConfig.paramsId as string] = isNaN(paramsId)
                ? Types.ObjectId(paramsId)
                : Number(paramsId);
            }
          },
        },
        {
          name: '排序/sort',
          field: EQueryParamsField.Sort,
          isAllowed: lodash.isUndefined(sort) || [ESortType.Asc, ESortType.Desc, ESortType.Hot].includes(sort),
          isIllegal: false,
          setValue() {
            options.sort = {
              _id: sort != null
                ? sort
                : ESortType.Desc,
            };
          },
        },
        {
          name: '目标页/page',
          field: EQueryParamsField.Page,
          isAllowed: lodash.isUndefined(page) || (lodash.isInteger(page) && Number(page) > 0),
          isIllegal: false,
          setValue() {
            if (page != null) {
              options.page = page;
            }
          },
        },
        {
          name: '每页数量/per_page',
          field: EQueryParamsField.PerPage,
          isAllowed: lodash.isUndefined(per_page) || (lodash.isInteger(per_page) && Number(per_page) > 0),
          isIllegal: false,
          setValue() {
            if (per_page != null) {
              options.limit = per_page;
            }
          },
        },
        {
          name: '日期查询/date',
          field: EQueryParamsField.Date,
          isAllowed: lodash.isUndefined(date) || new Date(date).toString() !== 'Invalid Date',
          isIllegal: false,
          setValue() {
            if (date != null) {
              const queryDate = new Date(date);
              querys.create_at = {
                $gte: new Date(((queryDate as any) / 1000 - 60 * 60 * 8) * 1000),
                $lt: new Date(((queryDate as any) / 1000 + 60 * 60 * 16) * 1000),
              };
            }
          },
        },
        {
          name: '发布状态/state', // 评论或其他数据
          field: EQueryParamsField.State,
          isAllowed: lodash.isUndefined(state) ||
            (transformConfig[EQueryParamsField.CommentState]
              ? [ECommentState.Auditing, ECommentState.Deleted, ECommentState.Published, ECommentState.Spam].includes(state)
              : [EPublishState.Published, EPublishState.Draft, EPublishState.Recycle].includes(state)
            ),
          isIllegal:
            !isAuthenticated &&
            state != null &&
            state !== (
              transformConfig[EQueryParamsField.CommentState]
                ? ECommentState.Published
                : EPublishState.Published
            ),
          setValue() {
            // 管理员/任意状态 || 普通用户/已发布
            if (state != null) {
              querys.state = state;
              return false;
            }
            // 普通用户/未设置
            if (!isAuthenticated) {
              querys.state = transformConfig[EQueryParamsField.CommentState]
                ? ECommentState.Published
                : EPublishState.Published;
            }
          },
        },
        {
          name: '公开状态/public',
          field: EQueryParamsField.Public,
          isAllowed: lodash.isUndefined(ppublic) || [EPublicState.Public, EPublicState.Password, EPublicState.Secret].includes(ppublic),
          isIllegal: ppublic != null && !isAuthenticated && ppublic !== EPublicState.Public,
          setValue() {
            // 管理员/任意状态 || 普通用户/公开
            if (ppublic != null) {
              querys.public = ppublic;
              return false;
            }
            // 普通用户/未设置
            if (!isAuthenticated) {
              querys.public = EPublicState.Public;
            }
          },
        },
        {
          name: '来源状态/origin',
          field: EQueryParamsField.Origin,
          isAllowed: lodash.isUndefined(origin) || [EOriginState.Original, EOriginState.Hybrid, EOriginState.Reprint].includes(origin),
          isIllegal: false,
          setValue() {
            if (origin != null) {
              querys.origin = origin;
            }
          },
        },
      ];
    
      // 验证字段是否被允许
      const isEnableField = field => field != null && field !== false;
    
      // 验证参数及生成参数
      validates.forEach(validate => {
        if (!isEnableField(transformConfig[validate.field])) {
          return false;
        }
        if (!validate.isAllowed) {
          throw new HttpBadRequestError('参数不合法:' + validate.name);
        }
        if (validate.isIllegal) {
          throw new HttpForbiddenError('权限与参数匹配不合法:' + validate.name);
        }
        validate.setValue();
      });
    
      /**
       * 处理剩余的规则外参数
       * 1. 用户传入配置与默认配置混合得到需要处理的参数字段
       * 2. 内置一堆关键参数的校验器
       * 3. 剩下的非内部校验的非关键参数,在此合并至 querys
       */
    
      // 已处理字段
      const isProcessedFields = validates.map(validate => validate.field);
      // 配置允许的字段
      const allAllowFields = Object.keys(transformConfig);
      // 剩余的待处理字段 = 配置允许的字段 - 已处理字段
      const todoFields = lodash.difference(allAllowFields, isProcessedFields);
      // 将所有待处理字段循环,将值循环至 querys
      todoFields.forEach(field => {
        const targetValue = request.query[field];
        if (targetValue != null) querys[field] = targetValue;
      });
    
      // 挂载到 request 上下文
      request.queryParams = { querys, options, params, isAuthenticated };
    
      // 来源 IP
      const ip = (
        request.headers['x-forwarded-for'] ||
        request.headers['x-real-ip'] ||
        request.connection.remoteAddress ||
        request.socket.remoteAddress ||
        request.connection.socket.remoteAddress ||
        request.ip ||
        request.ips[0]
      ).replace('::ffff:', '');
    
      // 用户标识
      const ua = request.headers['user-agent'];
    
      const result = {
        querys,
        options,
        params,
        request,
        origin: request.query,
        visitors: { ip, ua, referer: request.referer },
        isAuthenticated,
      };
    
      // console.log('queryParams\n', request.queryParams);
      // console.log('origin\n', request.query);
      // console.log('visitors\n', result.visitors);
    
      return result;
    });
    
    0
    打赏
    收藏
    点击回答
    您的回答被采纳后将获得:提问者悬赏的 11 元积分
        全部回答
    • 共 0 条
    • ._Angel妃°〞 普通会员 1楼

      In TypeScript, the type xxx cannot be used as an index type because it is not a valid type for an index signature. An index signature in TypeScript is a tuple of parameters that is used to declare a function or a method that takes a sequence of values (in this case, a type xxx).

      Index types are typically used to specify the position of an element within a sequence, such as an array, a dictionary, or a list. They are represented as tuples with two elements: the index and the value to be accessed at that index. Here's an example of an index signature in TypeScript: typescript [0, 1, 2, 3, 4, 5].map(x => x * 2)

      In this example, the index type xxx represents the index 0, which corresponds to the element in the array [0, 1, 2, 3, 4, 5] that we want to access using the map function. The value to be accessed at this index is x * 2, which represents the multiplication of the element at index 0 with x.

      However, if you try to use xxx as an index type in a for loop or a property assignment, you'll get a type error. Here's an example: typescript for (let i = 0; i < 6; i++) { console.log(i * xxx); // This line will throw a type error }

      In this example, we are trying to use xxx as the index type in a for loop that iterates over the elements in the array [0, 1, 2, 3, 4, 5]. The index i is an integer, which is a valid index type in TypeScript. However, when we try to access the element at index i * xxx, we get a type error because xxx is a type and cannot be used as an index type. To use an index type in a for loop or a property assignment, you need to specify the index value explicitly as an array or a number, like this: typescript for (let i = 0; i < 6; i++) { console.log(i + xxx); // This line will log the value at index 0, which is `0` }

      In this example, we use the array xxx to specify the index value, and the value is accessed using the addition operator +, which is a valid operator in TypeScript.

    更多回答
    扫一扫访问手机版
    • 回到顶部
    • 回到顶部