add user module and jwt
This commit is contained in:
parent
6fa6e1f076
commit
b8cc65a768
@ -1,4 +1,4 @@
|
|||||||
import { IsUUID, IsDefined } from 'class-validator';
|
import { IsDefined, IsUUID } from 'class-validator';
|
||||||
|
|
||||||
import { DtoValidation } from '@/modules/core/decorator/dto.validation.decorator';
|
import { DtoValidation } from '@/modules/core/decorator/dto.validation.decorator';
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Exclude, Expose, Type } from 'class-transformer';
|
import { Exclude, Expose, Type } from 'class-transformer';
|
||||||
|
import type { Relation } from 'typeorm';
|
||||||
import {
|
import {
|
||||||
BaseEntity,
|
BaseEntity,
|
||||||
Column,
|
Column,
|
||||||
@ -10,8 +11,6 @@ import {
|
|||||||
TreeParent,
|
TreeParent,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
|
|
||||||
import type { Relation } from 'typeorm';
|
|
||||||
|
|
||||||
import { PostEntity } from '@/modules/content/entities/post.entity';
|
import { PostEntity } from '@/modules/content/entities/post.entity';
|
||||||
|
|
||||||
@Exclude()
|
@Exclude()
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Exclude, Expose } from 'class-transformer';
|
import { Exclude, Expose } from 'class-transformer';
|
||||||
import { Column, Entity, ManyToMany, PrimaryColumn } from 'typeorm';
|
|
||||||
import type { Relation } from 'typeorm';
|
import type { Relation } from 'typeorm';
|
||||||
|
import { Column, Entity, ManyToMany, PrimaryColumn } from 'typeorm';
|
||||||
|
|
||||||
import { PostEntity } from '@/modules/content/entities/post.entity';
|
import { PostEntity } from '@/modules/content/entities/post.entity';
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import { QueryHook } from '@/modules/database/types';
|
|||||||
type FindCommentTreeOptions = FindTreeOptions & {
|
type FindCommentTreeOptions = FindTreeOptions & {
|
||||||
addQuery?: QueryHook<CommentEntity>;
|
addQuery?: QueryHook<CommentEntity>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@CustomRepository(CommentEntity)
|
@CustomRepository(CommentEntity)
|
||||||
export class CommentRepository extends BaseTreeRepository<CommentEntity> {
|
export class CommentRepository extends BaseTreeRepository<CommentEntity> {
|
||||||
protected _qbName = 'comment';
|
protected _qbName = 'comment';
|
||||||
|
@ -4,6 +4,7 @@ import { deepMerge } from '@/modules/core/helpers';
|
|||||||
|
|
||||||
export class SanitizeService {
|
export class SanitizeService {
|
||||||
protected config: sanitizeHtml.IOptions = {};
|
protected config: sanitizeHtml.IOptions = {};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.config = {
|
this.config = {
|
||||||
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img', 'code']),
|
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img', 'code']),
|
||||||
|
@ -13,6 +13,7 @@ export class MatchConstraint implements ValidatorConstraintInterface {
|
|||||||
const relatedValue = (validationArguments.object as any)[relatedProperty];
|
const relatedValue = (validationArguments.object as any)[relatedProperty];
|
||||||
return (value === relatedValue) !== reverse;
|
return (value === relatedValue) !== reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultMessage?(validationArguments?: ValidationArguments): string {
|
defaultMessage?(validationArguments?: ValidationArguments): string {
|
||||||
const [relatedProperty, reverse] = validationArguments.constraints;
|
const [relatedProperty, reverse] = validationArguments.constraints;
|
||||||
return `${relatedProperty} and ${validationArguments.property} ${
|
return `${relatedProperty} and ${validationArguments.property} ${
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
ValidationArguments,
|
|
||||||
ValidatorConstraintInterface,
|
|
||||||
ValidationOptions,
|
|
||||||
registerDecorator,
|
registerDecorator,
|
||||||
|
ValidationArguments,
|
||||||
|
ValidationOptions,
|
||||||
|
ValidatorConstraintInterface,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
|
|
||||||
type ModelType = 1 | 2 | 3 | 4 | 5;
|
type ModelType = 1 | 2 | 3 | 4 | 5;
|
||||||
@ -30,6 +30,7 @@ export class PasswordConstraint implements ValidatorConstraintInterface {
|
|||||||
return /\d/.test(value) && /[A-Za-z]/.test(value);
|
return /\d/.test(value) && /[A-Za-z]/.test(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultMessage?(validationArguments?: ValidationArguments): string {
|
defaultMessage?(validationArguments?: ValidationArguments): string {
|
||||||
return "($value) 's format error";
|
return "($value) 's format error";
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
|
registerDecorator,
|
||||||
ValidationArguments,
|
ValidationArguments,
|
||||||
|
ValidationOptions,
|
||||||
ValidatorConstraint,
|
ValidatorConstraint,
|
||||||
ValidatorConstraintInterface,
|
ValidatorConstraintInterface,
|
||||||
ValidationOptions,
|
|
||||||
registerDecorator,
|
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import { isArray, isNil } from 'lodash';
|
import { isArray, isNil } from 'lodash';
|
||||||
import { ObjectType, Repository, DataSource } from 'typeorm';
|
import { DataSource, ObjectType, Repository } from 'typeorm';
|
||||||
|
|
||||||
type Condition = {
|
type Condition = {
|
||||||
entity: ObjectType<any>;
|
entity: ObjectType<any>;
|
||||||
@ -52,6 +52,7 @@ export class DataExistConstraint implements ValidatorConstraintInterface {
|
|||||||
const item = await repo.findOne({ where: { [map]: value } });
|
const item = await repo.findOne({ where: { [map]: value } });
|
||||||
return !!item;
|
return !!item;
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultMessage?(validationArguments?: ValidationArguments): string {
|
defaultMessage?(validationArguments?: ValidationArguments): string {
|
||||||
if (!validationArguments.constraints[0]) {
|
if (!validationArguments.constraints[0]) {
|
||||||
return 'Model not been specified!';
|
return 'Model not been specified!';
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
|
registerDecorator,
|
||||||
|
ValidationArguments,
|
||||||
|
ValidationOptions,
|
||||||
ValidatorConstraint,
|
ValidatorConstraint,
|
||||||
ValidatorConstraintInterface,
|
ValidatorConstraintInterface,
|
||||||
ValidationArguments,
|
|
||||||
registerDecorator,
|
|
||||||
ValidationOptions,
|
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import { merge, isNil } from 'lodash';
|
import { isNil, merge } from 'lodash';
|
||||||
import { DataSource, ObjectType } from 'typeorm';
|
import { DataSource, ObjectType } from 'typeorm';
|
||||||
|
|
||||||
type Condition = {
|
type Condition = {
|
||||||
|
@ -2,9 +2,9 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import {
|
import {
|
||||||
registerDecorator,
|
registerDecorator,
|
||||||
ValidationArguments,
|
ValidationArguments,
|
||||||
|
ValidationOptions,
|
||||||
ValidatorConstraint,
|
ValidatorConstraint,
|
||||||
ValidatorConstraintInterface,
|
ValidatorConstraintInterface,
|
||||||
ValidationOptions,
|
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import { isNil, merge } from 'lodash';
|
import { isNil, merge } from 'lodash';
|
||||||
import { DataSource, ObjectType } from 'typeorm';
|
import { DataSource, ObjectType } from 'typeorm';
|
||||||
@ -39,6 +39,7 @@ export class UniqueConstraint implements ValidatorConstraintInterface {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultMessage?(validationArguments?: ValidationArguments): string {
|
defaultMessage?(validationArguments?: ValidationArguments): string {
|
||||||
const { entity, property } = validationArguments.constraints[0];
|
const { entity, property } = validationArguments.constraints[0];
|
||||||
const queryProperty = property ?? validationArguments.property;
|
const queryProperty = property ?? validationArguments.property;
|
||||||
|
@ -18,6 +18,7 @@ type Condition = {
|
|||||||
|
|
||||||
property?: string;
|
property?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ValidatorConstraint({ name: 'dataUniqueExist', async: true })
|
@ValidatorConstraint({ name: 'dataUniqueExist', async: true })
|
||||||
export class UniqueExistConstraint implements ValidatorConstraintInterface {
|
export class UniqueExistConstraint implements ValidatorConstraintInterface {
|
||||||
@ -48,6 +49,7 @@ export class UniqueExistConstraint implements ValidatorConstraintInterface {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultMessage?(args?: ValidationArguments): string {
|
defaultMessage?(args?: ValidationArguments): string {
|
||||||
const { entity, property } = args.constraints[0];
|
const { entity, property } = args.constraints[0];
|
||||||
const queryProperty = property ?? args.property;
|
const queryProperty = property ?? args.property;
|
||||||
|
@ -24,7 +24,7 @@ import { DBOptions } from './types';
|
|||||||
export class DatabaseModule {
|
export class DatabaseModule {
|
||||||
static async forRoot(configure: Configure): Promise<DynamicModule> {
|
static async forRoot(configure: Configure): Promise<DynamicModule> {
|
||||||
if (!configure.has('database')) {
|
if (!configure.has('database')) {
|
||||||
panic({ message: 'Database config not exists' });
|
await panic({ message: 'Database config not exists' });
|
||||||
}
|
}
|
||||||
const { connections } = await configure.get<DBOptions>('database');
|
const { connections } = await configure.get<DBOptions>('database');
|
||||||
const imports: ModuleMetadata['imports'] = [];
|
const imports: ModuleMetadata['imports'] = [];
|
||||||
@ -46,6 +46,7 @@ export class DatabaseModule {
|
|||||||
providers,
|
providers,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static forRepository<T extends Type<any>>(
|
static forRepository<T extends Type<any>>(
|
||||||
repositories: T[],
|
repositories: T[],
|
||||||
datasourceName?: string,
|
datasourceName?: string,
|
||||||
|
115
src/modules/user/services/auth.service.ts
Normal file
115
src/modules/user/services/auth.service.ts
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/* eslint-disable import/no-extraneous-dependencies */
|
||||||
|
import { ForbiddenException, Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { FastifyRequest as Request } from 'fastify';
|
||||||
|
|
||||||
|
import { ExtractJwt } from 'passport-jwt';
|
||||||
|
|
||||||
|
import { Configure } from '@/modules/config/configure';
|
||||||
|
|
||||||
|
import { getTime } from '@/modules/core/helpers/time';
|
||||||
|
import { RegisterDto } from '@/modules/user/dtos/auth.dto';
|
||||||
|
import { UserEntity } from '@/modules/user/entities/UserEntity';
|
||||||
|
import { TokenService } from '@/modules/user/services/token.service';
|
||||||
|
import { decrypt } from '@/modules/user/utils';
|
||||||
|
|
||||||
|
import { UpdatePasswordDto } from '../dtos/account.dto';
|
||||||
|
import { UserRepository } from '../repositories/UserRepository';
|
||||||
|
|
||||||
|
import { UserService } from './user.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuthService {
|
||||||
|
constructor(
|
||||||
|
protected configure: Configure,
|
||||||
|
protected userService: UserService,
|
||||||
|
protected tokenService: TokenService,
|
||||||
|
protected userRepository: UserRepository,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登录验证
|
||||||
|
* @param credential
|
||||||
|
* @param password
|
||||||
|
*/
|
||||||
|
async validateUser(credential: string, password: string) {
|
||||||
|
const user = await this.userService.findOneByCredential(credential, async (query) =>
|
||||||
|
query.addSelect('user.password'),
|
||||||
|
);
|
||||||
|
if (user && decrypt(password, user.password)) {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录用户,并生成新的accessToken和refreshToken
|
||||||
|
* @param user
|
||||||
|
*/
|
||||||
|
async login(user: UserEntity) {
|
||||||
|
const now = await getTime(this.configure);
|
||||||
|
const { accessToken } = await this.tokenService.generateAccessToken(user, now);
|
||||||
|
return accessToken.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注销登录
|
||||||
|
* @param req
|
||||||
|
*/
|
||||||
|
async logout(req: Request) {
|
||||||
|
const accessToken = ExtractJwt.fromAuthHeaderAsBearerToken()(req as any);
|
||||||
|
if (accessToken) {
|
||||||
|
await this.tokenService.removeAccessToken(accessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { msg: 'logout_success' };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录用户后生成新的token和refreshToken
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
async createToken(id: string) {
|
||||||
|
const now = await getTime(this.configure);
|
||||||
|
let user: UserEntity;
|
||||||
|
try {
|
||||||
|
user = await this.userService.detail(id);
|
||||||
|
} catch (err) {
|
||||||
|
throw new ForbiddenException(err);
|
||||||
|
}
|
||||||
|
const { accessToken } = await this.tokenService.generateAccessToken(user, now);
|
||||||
|
return accessToken.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用用户名密码注册用户
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
async register(data: RegisterDto) {
|
||||||
|
const { username, nickname, password } = data;
|
||||||
|
const user = await this.userService.create({
|
||||||
|
username,
|
||||||
|
nickname,
|
||||||
|
password,
|
||||||
|
} as any);
|
||||||
|
return this.userService.findOneByCondition({ id: user.id });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新用户密码
|
||||||
|
* @param user
|
||||||
|
* @param password
|
||||||
|
* @param oldPassword
|
||||||
|
*/
|
||||||
|
async changePassword(user: UserEntity, { password, oldPassword }: UpdatePasswordDto) {
|
||||||
|
const item = await this.userRepository.findOneOrFail({
|
||||||
|
select: ['password'],
|
||||||
|
where: { id: user.id },
|
||||||
|
});
|
||||||
|
if (decrypt(oldPassword, item.password)) {
|
||||||
|
await this.userRepository.save({ id: user.id, password }, { reload: true });
|
||||||
|
return this.userService.detail(user.id);
|
||||||
|
}
|
||||||
|
throw new ForbiddenException('old password do not match');
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable import/no-extraneous-dependencies */
|
/* eslint-disable import/no-extraneous-dependencies */
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
import { JwtService } from '@nestjs/jwt';
|
import { JwtModule, JwtModuleOptions, JwtService } from '@nestjs/jwt';
|
||||||
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { FastifyReply as Response } from 'fastify';
|
import { FastifyReply as Response } from 'fastify';
|
||||||
@ -10,11 +10,12 @@ import { v4 as uuid } from 'uuid';
|
|||||||
|
|
||||||
import { Configure } from '@/modules/config/configure';
|
import { Configure } from '@/modules/config/configure';
|
||||||
import { getTime } from '@/modules/core/helpers/time';
|
import { getTime } from '@/modules/core/helpers/time';
|
||||||
import { getUserConfig } from '@/modules/user/config';
|
import { defaultUserConfig, getUserConfig } from '@/modules/user/config';
|
||||||
import { UserEntity } from '@/modules/user/entities/UserEntity';
|
import { UserEntity } from '@/modules/user/entities/UserEntity';
|
||||||
import { AccessTokenEntity } from '@/modules/user/entities/access.token.entity';
|
import { AccessTokenEntity } from '@/modules/user/entities/access.token.entity';
|
||||||
import { RefreshTokenEntity } from '@/modules/user/entities/refresh.token.entity';
|
import { RefreshTokenEntity } from '@/modules/user/entities/refresh.token.entity';
|
||||||
import { JwtConfig, JwtPayload } from '@/modules/user/types';
|
import { JwtConfig, JwtPayload, UserConfig } from '@/modules/user/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 令牌服务
|
* 令牌服务
|
||||||
*/
|
*/
|
||||||
@ -142,4 +143,25 @@ export class TokenService {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JwtModuleFactory(configure: Configure) {
|
||||||
|
return JwtModule.registerAsync({
|
||||||
|
useFactory: async (): Promise<JwtModuleOptions> => {
|
||||||
|
const config = await configure.get<UserConfig>(
|
||||||
|
'user',
|
||||||
|
defaultUserConfig(configure),
|
||||||
|
);
|
||||||
|
const options: JwtModuleOptions = {
|
||||||
|
secret: configure.env.get('USER_TOKEN_SECRET', 'my-access-secret'),
|
||||||
|
verifyOptions: {
|
||||||
|
ignoreExpiration: !configure.env.isProd(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (configure.env.isProd()) {
|
||||||
|
options.signOptions = { expiresIn: `${config.jwt.tokenExpired}s` };
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
94
src/modules/user/services/user.service.ts
Normal file
94
src/modules/user/services/user.service.ts
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { isNil } from 'lodash';
|
||||||
|
import { DataSource, EntityNotFoundError, SelectQueryBuilder } from 'typeorm';
|
||||||
|
|
||||||
|
import { Configure } from '@/modules/config/configure';
|
||||||
|
import { BaseService } from '@/modules/database/base/service';
|
||||||
|
|
||||||
|
import { QueryHook } from '@/modules/database/types';
|
||||||
|
|
||||||
|
import { CreateUserDto, QueryUserDto, UpdateUserDto } from '../dtos/user.dto';
|
||||||
|
import { UserEntity } from '../entities/UserEntity';
|
||||||
|
import { UserRepository } from '../repositories/UserRepository';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UserService extends BaseService<UserEntity, UserRepository> {
|
||||||
|
protected enableTrash = true;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected configure: Configure,
|
||||||
|
protected dataSource: DataSource,
|
||||||
|
protected userRepository: UserRepository,
|
||||||
|
) {
|
||||||
|
super(userRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建用户
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
async create(data: CreateUserDto): Promise<UserEntity> {
|
||||||
|
const user = await this.userRepository.save(data, { reload: true });
|
||||||
|
return this.detail(user.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新用户
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
async update(data: UpdateUserDto): Promise<UserEntity> {
|
||||||
|
const updated = await this.userRepository.save(data, { reload: true });
|
||||||
|
return this.detail(updated.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户用户凭证查询用户
|
||||||
|
* @param credential
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
async findOneByCredential(credential: string, callback?: QueryHook<UserEntity>) {
|
||||||
|
let query = this.userRepository.buildBaseQuery();
|
||||||
|
if (callback) {
|
||||||
|
query = await callback(query);
|
||||||
|
}
|
||||||
|
return query
|
||||||
|
.where('user.username = :credential', { credential })
|
||||||
|
.orWhere('user.email = :credential', { credential })
|
||||||
|
.orWhere('user.phone = :credential', { credential })
|
||||||
|
.getOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据对象条件查找用户,不存在则抛出异常
|
||||||
|
* @param condition
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
async findOneByCondition(condition: { [key: string]: any }, callback?: QueryHook<UserEntity>) {
|
||||||
|
let query = this.userRepository.buildBaseQuery();
|
||||||
|
if (callback) {
|
||||||
|
query = await callback(query);
|
||||||
|
}
|
||||||
|
const wheres = Object.fromEntries(
|
||||||
|
Object.entries(condition).map(([key, value]) => [key, value]),
|
||||||
|
);
|
||||||
|
const user = query.where(wheres).getOne();
|
||||||
|
if (!user) {
|
||||||
|
throw new EntityNotFoundError(UserEntity, Object.keys(condition).join(','));
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async buildListQB(
|
||||||
|
queryBuilder: SelectQueryBuilder<UserEntity>,
|
||||||
|
options: QueryUserDto,
|
||||||
|
callback?: QueryHook<UserEntity>,
|
||||||
|
) {
|
||||||
|
const { orderBy } = options;
|
||||||
|
const qb = await super.buildListQB(queryBuilder, options, callback);
|
||||||
|
if (!isNil(orderBy)) {
|
||||||
|
qb.orderBy(`${this.repository.qbName}.${orderBy}`, 'ASC');
|
||||||
|
}
|
||||||
|
return qb;
|
||||||
|
}
|
||||||
|
}
|
16
typings/global.d.ts
vendored
16
typings/global.d.ts
vendored
@ -15,20 +15,20 @@ declare type RePartial<T> = {
|
|||||||
[P in keyof T]?: T[P] extends (infer U)[] | undefined
|
[P in keyof T]?: T[P] extends (infer U)[] | undefined
|
||||||
? RePartial<U>[]
|
? RePartial<U>[]
|
||||||
: T[P] extends object | undefined
|
: T[P] extends object | undefined
|
||||||
? T[P] extends ((...args: any[]) => any) | ClassType<T[P]> | undefined
|
? T[P] extends ((...args: any[]) => any) | ClassType<T[P]> | undefined
|
||||||
? T[P]
|
? T[P]
|
||||||
: RePartial<T[P]>
|
: RePartial<T[P]>
|
||||||
: T[P];
|
: T[P];
|
||||||
};
|
};
|
||||||
|
|
||||||
declare type ReRequired<T> = {
|
declare type ReRequired<T> = {
|
||||||
[P in keyof T]-?: T[P] extends (infer U)[] | undefined
|
[P in keyof T]-?: T[P] extends (infer U)[] | undefined
|
||||||
? ReRequired<U>[]
|
? ReRequired<U>[]
|
||||||
: T[P] extends object | undefined
|
: T[P] extends object | undefined
|
||||||
? T[P] extends ((...args: any[]) => any) | ClassType<T[P]> | undefined
|
? T[P] extends ((...args: any[]) => any) | ClassType<T[P]> | undefined
|
||||||
? T[P]
|
? T[P]
|
||||||
: ReRequired<T[P]>
|
: ReRequired<T[P]>
|
||||||
: T[P];
|
: T[P];
|
||||||
};
|
};
|
||||||
|
|
||||||
declare type WrapperType<T> = T;
|
declare type WrapperType<T> = T;
|
||||||
|
Loading…
Reference in New Issue
Block a user