TypeScript 中类似反射的密钥到字符串的转换?
Reflection-like conversion of key to a string in TypeScript?
我用的是Proxy
object with the idea that whenever a property gets updated, some other side-effect can also be initiated. I don't want to initiate the side effects in the many places that properties get set (DRY principle).
有点做作的示例代码:
const session = new Proxy(
{ id: undefined as number, age: undefined as number}, // =target
{
set: (target, property, value): boolean => {
switch (property) {
case 'id': {
target[property] = value;
this.notifyIdWasUpdated(value);
return true;
}
case 'age': {
target[property] = value;
this.updateAgeDisplay(value);
return true;
}
default: {
return false;
}
}
}
}
);
我的问题是,当我使用 IDE 的重构更改 target
的 属性 名称(键)时对象(例如 age
),case
语句中的字符串常量(例如 'age'
) 不 也得到更新(可能导致错误)。
问题:有没有办法从case
语句中的表达式obj.key
动态获取字符串值'key'
(其中然后将是重构证明)? ( 某种 与 ['key']
访问器相反,在某种程度上...)或者,您能否建议另一种构建上述代码的方法以防止这种程序员监督?
- 我找到了 Get object property name as a string,但想知道是否有更“不确定”的解决方案 - 恕我直言,在潜在问题和添加大量代码来防范它之间进行权衡是不值得的。 (许多技术似乎遍历所有键并匹配 属性 类型或值;这些不够安全。)
- Typescript's documentation seems to say 用于类反射用途的元数据发射尚未正式采用。恕我直言,为此添加整个实验库也不值得。
你可以在这里尝试使用keyof
。
interface Session {
id: number
age: number
}
const session1 = new Proxy(
{ id: 0, age: 0 } as Session,
{
set: (target, property: keyof Session, value): boolean => {
switch (property) {
case 'id': {
target[property] = value;
this.notifyIdWasUpdated(value);
return true;
}
case 'age': {
target[property] = value;
this.updateAgeDisplay(value);
return true;
}
default: {
return false;
}
}
}
}
);
这不会自动重命名,但如果 case
中的 属性 在 Session
中不存在,typescript 将显示错误。
以下情况应允许自动重命名:
interface Session {
id: number
age: number
}
type Handlers<Model> = {
[Key in keyof Model]: (newValue: Model[Key]) => void;
}
// Partial<Handlers<Session>> in case you don't want to handle each property
const handlers: Handlers<Session> = {
id: () => { },
age: () => { },
}
const session = new Proxy(
{ id: 0, age: 0 } as Session,
{
set: (target, property: keyof Session, value): boolean => {
const handler = handlers[property];
if (handler) {
handler(value)
return true;
}
return false;
}
}
);
简单的解决方案是这样的:
function nameof<TType>(selector: (t?: TType) => any) {
const match = selector
.toString()
.match(/=>\s*(?:[a-zA-Z0-9_$]+\.?)*?([a-zA-Z0-9_$]+)$/);
if (match) {
return match[1];
}
return undefined;
}
interface MyType {
id: any;
age: number;
}
const session = new Proxy(
{ id: undefined as number, age: undefined as number }, // =target
{
set: (target, property, value): boolean => {
switch (property) {
case nameof<MyType>((t) => t.id): {
target[property] = value;
this.notifyIdWasUpdated(value);
return true;
}
case nameof<MyType>((t) => t.age): {
target[property] = value;
this.updateAgeDisplay(value);
return true;
}
default: {
return false;
}
}
},
}
);
注意:小心,如果你的目标是 ES5!箭头函数被转译为带有 return
的常规函数,因此正则表达式将不起作用,您必须更改正则表达式。
虽然选择了另一个 ,但给定示例代码的问题在更高的抽象级别上。由于session
对象封装了很多属性,所以session
对象应该作为一个单元来处理,而不是属性分开处理。 (可能有一个代码气味名称或其他一些针对此的警告...)
样本将只是:
session = new Proxy(
{ id: undefined, age: undefined}, // =target
{
set: (target, property, value): boolean => {
if (typeof property === 'string' && Object.keys(target).includes(<string>property)) {
target[property] = value;
doSideEffects(target);
return true;
} else {
return false;
}
},
}
);
这简化了代理中的处理程序。
(我是 OP。在我的例子中,它现在也大大简化了副作用代码。我想橡皮鸭效应开始发挥作用了...)
我用的是Proxy
object with the idea that whenever a property gets updated, some other side-effect can also be initiated. I don't want to initiate the side effects in the many places that properties get set (DRY principle).
有点做作的示例代码:
const session = new Proxy(
{ id: undefined as number, age: undefined as number}, // =target
{
set: (target, property, value): boolean => {
switch (property) {
case 'id': {
target[property] = value;
this.notifyIdWasUpdated(value);
return true;
}
case 'age': {
target[property] = value;
this.updateAgeDisplay(value);
return true;
}
default: {
return false;
}
}
}
}
);
我的问题是,当我使用 IDE 的重构更改 target
的 属性 名称(键)时对象(例如 age
),case
语句中的字符串常量(例如 'age'
) 不 也得到更新(可能导致错误)。
问题:有没有办法从case
语句中的表达式obj.key
动态获取字符串值'key'
(其中然后将是重构证明)? ( 某种 与 ['key']
访问器相反,在某种程度上...)或者,您能否建议另一种构建上述代码的方法以防止这种程序员监督?
- 我找到了 Get object property name as a string,但想知道是否有更“不确定”的解决方案 - 恕我直言,在潜在问题和添加大量代码来防范它之间进行权衡是不值得的。 (许多技术似乎遍历所有键并匹配 属性 类型或值;这些不够安全。)
- Typescript's documentation seems to say 用于类反射用途的元数据发射尚未正式采用。恕我直言,为此添加整个实验库也不值得。
你可以在这里尝试使用keyof
。
interface Session {
id: number
age: number
}
const session1 = new Proxy(
{ id: 0, age: 0 } as Session,
{
set: (target, property: keyof Session, value): boolean => {
switch (property) {
case 'id': {
target[property] = value;
this.notifyIdWasUpdated(value);
return true;
}
case 'age': {
target[property] = value;
this.updateAgeDisplay(value);
return true;
}
default: {
return false;
}
}
}
}
);
这不会自动重命名,但如果 case
中的 属性 在 Session
中不存在,typescript 将显示错误。
以下情况应允许自动重命名:
interface Session {
id: number
age: number
}
type Handlers<Model> = {
[Key in keyof Model]: (newValue: Model[Key]) => void;
}
// Partial<Handlers<Session>> in case you don't want to handle each property
const handlers: Handlers<Session> = {
id: () => { },
age: () => { },
}
const session = new Proxy(
{ id: 0, age: 0 } as Session,
{
set: (target, property: keyof Session, value): boolean => {
const handler = handlers[property];
if (handler) {
handler(value)
return true;
}
return false;
}
}
);
简单的解决方案是这样的:
function nameof<TType>(selector: (t?: TType) => any) {
const match = selector
.toString()
.match(/=>\s*(?:[a-zA-Z0-9_$]+\.?)*?([a-zA-Z0-9_$]+)$/);
if (match) {
return match[1];
}
return undefined;
}
interface MyType {
id: any;
age: number;
}
const session = new Proxy(
{ id: undefined as number, age: undefined as number }, // =target
{
set: (target, property, value): boolean => {
switch (property) {
case nameof<MyType>((t) => t.id): {
target[property] = value;
this.notifyIdWasUpdated(value);
return true;
}
case nameof<MyType>((t) => t.age): {
target[property] = value;
this.updateAgeDisplay(value);
return true;
}
default: {
return false;
}
}
},
}
);
注意:小心,如果你的目标是 ES5!箭头函数被转译为带有 return
的常规函数,因此正则表达式将不起作用,您必须更改正则表达式。
虽然选择了另一个 session
对象封装了很多属性,所以session
对象应该作为一个单元来处理,而不是属性分开处理。 (可能有一个代码气味名称或其他一些针对此的警告...)
样本将只是:
session = new Proxy(
{ id: undefined, age: undefined}, // =target
{
set: (target, property, value): boolean => {
if (typeof property === 'string' && Object.keys(target).includes(<string>property)) {
target[property] = value;
doSideEffects(target);
return true;
} else {
return false;
}
},
}
);
这简化了代理中的处理程序。
(我是 OP。在我的例子中,它现在也大大简化了副作用代码。我想橡皮鸭效应开始发挥作用了...)