TypeScript 缩短通用函数调用
Posted
技术标签:
【中文标题】TypeScript 缩短通用函数调用【英文标题】:TypeScript shorten generic function call 【发布时间】:2021-11-18 16:52:29 【问题描述】:我在节点14.17.6
上使用Sequelize 6.6.5
和TypeScript 4.4.3
db/models/User.ts
(仅相关部分)
import
Sequelize,
Model,
DataTypes,
Optional,
ModelAttributeColumnOptions,
from 'sequelize';
import FlagPositions, Flags from '../types';
export interface IUserFlags
isDev: boolean;
isAdmin: boolean;
isMod: boolean;
export const flagPositions: FlagPositions<IUserFlags> =
isDev: 0,
isAdmin: 1,
isMod: 2,
;
export interface IUserAttributes extends Flags, IUserFlags
id: number;
name: string;
email: string;
// ...
export interface IUserCreationAttributes
extends Optional<
IUserAttributes,
| 'id'
| 'flags'
| 'isDev'
| 'isAdmin'
| 'isMod'
>
export class User
extends Model<IUserAttributes, IUserCreationAttributes>
implements IUserAttributes
public id!: number;
public name!: string;
public email!: string;
public flags!: number;
public isDev!: boolean;
public isAdmin!: boolean;
public isMod!: boolean;
// ...
export function init(sequelize: Sequelize): void
User.init(
id:
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
unique: true,
,
name:
type: DataTypes.STRING,
allowNull: false,
unique: true,
,
email:
type: DataTypes.STRING,
allowNull: false,
unique: true,
,
// ...
flags:
type: DataTypes.INTEGER,
defaultValue: 0,
,
isDev:
type: DataTypes.VIRTUAL,
get(): boolean
return !!((this.flags >> flagPositions.isDev) & 1);
,
set(value: boolean): void
const flags = this.getDataValue('flags');
const mask = 1 << flagPositions.isDev;
this.setDataValue('flags', value ? flags | mask : flags & ~mask);
,
,
isAdmin:
type: DataTypes.VIRTUAL,
get(): boolean
return !!((this.flags >> flagPositions.isAdmin) & 1);
,
set(value: boolean): void
const flags = this.getDataValue('flags');
const mask = 1 << flagPositions.isAdmin;
this.setDataValue('flags', value ? flags | mask : flags & ~mask);
,
,
isMod:
type: DataTypes.VIRTUAL,
get(): boolean
return !!((this.flags >> flagPositions.isMod) & 1);
,
set(value: boolean): void
const flags = this.getDataValue('flags');
const mask = 1 << flagPositions.isMod;
this.setDataValue('flags', value ? flags | mask : flags & ~mask);
,
,
,
sequelize, tableName: 'users' ,
);
../types
interface Flags
flags: number;
type FlagPositions<F extends Record<keyof F, boolean>> =
[K in keyof F]: number;
;
如您所见,flag virtuals 的代码基本上是重复的(WET)。
我想要这样的东西
isDev: createFlagVirtual<IUserFlags>(flagPositions, 'isDev');
经过一长串错误,我想出了
function createFlagVirtual<
TModelAttributes extends Flags,
TModelCreationAttributes extends Partial<TModelAttributes>,
TModel extends Model<TModelAttributes, TModelCreationAttributes> &
TModelAttributes,
TFlags extends Record<keyof TFlags, boolean>,
TFlag extends keyof TFlags = keyof TFlags,
>(
positions: FlagPositions<TFlags>,
flag: TFlag,
): ModelAttributeColumnOptions<TModel>
return
type: DataTypes.VIRTUAL,
get(): boolean
return !!((this.flags >> positions[flag]) & 1);
,
set(value: boolean): void
const flags = this.getDataValue('flags');
const mask = 1 << positions[flag];
this.setDataValue('flags', value ? flags | mask : flags & ~mask);
,
;
想知道我怎么称呼它?:
createFlagVirtual<IUserAttributes, IUserCreationAttributes, User, IUserFlags>(
flagPositions,
'isMod',
);
是的;没那么干。
除此之外,typescript 仍然不知道'flags'
可以传递给this.getDataValue
,至少根据 vscode 智能感知。但是它知道直接访问this.flags
是一个number
。
我想知道如何缩短对createFlagVirtual
的调用,以便以下工作createFlagVirtual<IUserFlags>(flagPositions, 'isDev');
。 TypeScript 应该知道什么可以传递给第二个参数,也就是 keyof
IUserFlags
。另外,在实现中,this.getDataValue
应该知道'flags'
是一个值。
【问题讨论】:
【参考方案1】:我自己想通了
function createFlagVirtual<
TFlags extends Record<keyof TFlags, boolean>,
TModel extends Model<Flags, > & Flags = Model<Flags, > & Flags,
>(
positions: FlagPositions<TFlags>,
name: keyof TFlags,
): ModelAttributeColumnOptions<TModel>
return
type: DataTypes.VIRTUAL,
get(): boolean
return !!((this.getDataValue('flags') >> positions[name]) & 1);
,
set(value: boolean): void
const flags = this.getDataValue('flags');
const mask = 1 << positions[name];
this.setDataValue('flags', value ? flags | mask : flags & ~mask);
,
;
用作
createFlagVirtual<IUserFlags>(flagPositions, 'isDev')
// or
createFlagVirtual<IUserFlags, User>(flagPositions, 'isDev'),
关键部分是TModel extends Model<Flags, > & Flags = Model<Flags, > & Flags
。
Model<Flags, >
让this.getDataValue
知道'flags'
是一个允许的值,& Flags
让 typescript 知道直接this.flags
访问是number
。
编辑:这显示了 TypeScript 可能是多么混乱,以及它如何通过让程序员专注于最终无关紧要的事情来实际减慢开发速度。我再次将我的代码库移动到 vanilla javascript。
【讨论】:
以上是关于TypeScript 缩短通用函数调用的主要内容,如果未能解决你的问题,请参考以下文章
Typescript:如何使用回调调用 Javascript 函数
在构造函数之前调用 Typescript/javascript 方法
如何在 TypeScript 中将函数作为变量调用 [关闭]