Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Typescript 获取泛型';d对象_Typescript_Types_Helper - Fatal编程技术网

Typescript 获取泛型';d对象

Typescript 获取泛型';d对象,typescript,types,helper,Typescript,Types,Helper,我试图制作一个函数,简化对象上可用于任意目的的setter函数的设置。明显的用途包括类型保护、值和绑定检查或触发事件 这样做的目的是获取一个已知形状的对象,并调用一个函数,该函数返回一个具有相同可枚举形状的新对象,该对象的每个成员实际上都具有set和get“属性”。setters来自函数的第二个参数,getters只返回“protected”值 这种方法的大量实用性来自于可能的严格打字。当受保护对象是在代码的单独部分中定义的,甚至可能超出您的控制时,这一点特别有用。如果对象形状发生更改,类型错误

我试图制作一个函数,简化对象上可用于任意目的的
set
ter函数的设置。明显的用途包括类型保护、值和绑定检查或触发事件

这样做的目的是获取一个已知形状的对象,并调用一个函数,该函数返回一个具有相同可枚举形状的新对象,该对象的每个成员实际上都具有
set
get
“属性”。
set
ters来自函数的第二个参数,
get
ters只返回“protected”值

这种方法的大量实用性来自于可能的严格打字。当受保护对象是在代码的单独部分中定义的,甚至可能超出您的控制时,这一点特别有用。如果对象形状发生更改,类型错误将确保添加/删除新的
set
ters

我很容易创建了一个“平面”版本

平版 当目标对象有嵌套对象时,棘手的部分就来了。虽然可以使用平面版本,但嵌套越深,编写就越麻烦

所以,我试图编写一个平面函数的递归版本。这将检测成员的类型是否为对象,并以不同的方式进行处理。我相信我的函数签名类型是正确的,但是在函数内部的一行中有一个我无法理解的硬错误

我知道TypeScript类型在运行时不可用,但我相信这是编译时的问题。我相信,如果我在注释行中获得正确的语法,它将起作用。但也许我错了

递归版本
type NestedSetters={[P in keyof T]:T[P]扩展了{}?NestedSetters:(next:T[P])=>void};
函数makeObjectSetterrCursive(内部:T,setters:NestedSetters){
常量ret={};
用于(内部的常量x){
let prop:PropertyDescriptor;
//我想不出这条线
类型t=内部类型[x];
//非常确定这个测试对于我来说是正确的运行时测试
if(内部[x]的类型=='object'){
道具={
value:makeObjectSetterrCursive(内部[x],setters[x]作为嵌套setters),//应该能够避免这种'as'强制转换,不是吗?
};
}否则{
道具={
set:setters[x]as(next:t)=>void,//应该能够避免这个'as'类型,不是吗?
获取:()=>内部[x],
};
}
prop.enumerable=true;
对象定义属性(ret、x、prop);
}
返回ret as T;//额外奖金摆脱此'as'角色
}
除了
typeof internal[x]
之外,我还尝试了
Pick
和其他猜测,但都无济于事

如果您对此有任何想法,我们将不胜感激。答案可能是我想要的是不可能的


侧任务:我觉得在正确的类型提示下,
as
强制转换是不必要的。

正如您所提到的,TypeScript类型在运行时不可用,因此,
typeof internal[x]
无法工作。您要查找的是
T[Extract]
,它提取道具值的类型

关于强制转换问题,似乎存在条件类型缩小的问题。 因此,必须依赖if-else语句中映射正确类型的运行时逻辑(在本例中为
typeof-value===“object”
)。对于语义,我认为创建一个用户定义的类型guard
isNestedSetters
并将值强制转换为联合类型
NestedSetters | Setter
,这很有用,因为编译器可以正确地缩小范围

以下是完整版本:

type Setter<T> = T extends boolean ? (next: boolean) => void : (next: T) => void

type SetterOrNested<T> = T extends object ? NestedSetters<T> : Setter<T>

type NestedSetters<T> = {
  [P in keyof T]: SetterOrNested<T[P]>
}

function isNestedSetters<T>(value: any): value is NestedSetters<T> {
  return typeof value === 'object';
}

function makeObjectSetterRecursive<T extends {}>(internal: T, setters: NestedSetters<T>) {
  const ret = <T>{};

  for (const x in internal) {
    let prop: PropertyDescriptor;

    type P = Extract<keyof T, string>

    const setterOrNested = setters[x] as NestedSetters<T[P]> | Setter<T[P]>

    if (isNestedSetters<T[P]>(setterOrNested)) {
      prop = {
        value: makeObjectSetterRecursive(internal[x], setterOrNested),
      };
    } else {
      prop = {
        set: setterOrNested,
        get: () => internal[x],
      };
    }

    prop.enumerable = true;

    Object.defineProperty(ret, x, prop);
  }

  return ret;
}

我必须说,我不太确定这个用例,但它是一个有趣的概念,所以我决定尝试一下。

是的!
摘录
是我所缺少的。看起来我的代码中的setter对象也存在键入问题,但您的解决方案解决了这个问题!我正在考虑将此代码打包为可重用的npm模块。你介意我使用你的实现吗?我很乐意把你列入贡献者名单(或者你认为合适的地方)。如果你想带着它跑,我也会很高兴的。当然,去吧。贡献者名单对我来说很有意义。让我保持联系:)
const myObject = {
  num: 42,
  str: 'initialValue',
};

const protectedObject = makeObjectSetter(myObject, {
  num(x) {
    // Make sure positive
    myObject.num = Math.max(x, 0);
  },
  str(s) {
    // Always double the input
    myObject.str = s + s;
  },
});

console.log(myObject);
// { num: 42, str: 'initialValue' }

protectedObject.num = -1;
protectedObject.str = 'a';

console.log(myObject);
// { num: 0, str: 'aa' }

for (let x in protectedObject) console.log(x);
// num
// str
type NestedSetters<T extends {}> = { [P in keyof T]: T[P] extends {} ? NestedSetters<T[P]> : (next: T[P]) => void };

function makeObjectSetterRecursive<T extends {}>(internal: T, setters: NestedSetters<T>) {
  const ret = {};

  for (const x in internal) {
    let prop: PropertyDescriptor;

    // Can't figure out this line
    type t = typeof internal[x];

    // Pretty sure this test is the right runtime test for my purposes
    if (typeof internal[x] == 'object') {
      prop = {
        value: makeObjectSetterRecursive(internal[x], setters[x] as NestedSetters<t>), // Should be able to avoid this `as` cast, no?
      };
    } else {
      prop = {
        set: setters[x] as (next: t) => void, // Should be able to avoid this `as` cast, no?
        get: () => internal[x],
      };
    }

    prop.enumerable = true;

    Object.defineProperty(ret, x, prop);
  }

  return ret as T; // Extra extra bonus get rid of this `as` cast
}
type Setter<T> = T extends boolean ? (next: boolean) => void : (next: T) => void

type SetterOrNested<T> = T extends object ? NestedSetters<T> : Setter<T>

type NestedSetters<T> = {
  [P in keyof T]: SetterOrNested<T[P]>
}

function isNestedSetters<T>(value: any): value is NestedSetters<T> {
  return typeof value === 'object';
}

function makeObjectSetterRecursive<T extends {}>(internal: T, setters: NestedSetters<T>) {
  const ret = <T>{};

  for (const x in internal) {
    let prop: PropertyDescriptor;

    type P = Extract<keyof T, string>

    const setterOrNested = setters[x] as NestedSetters<T[P]> | Setter<T[P]>

    if (isNestedSetters<T[P]>(setterOrNested)) {
      prop = {
        value: makeObjectSetterRecursive(internal[x], setterOrNested),
      };
    } else {
      prop = {
        set: setterOrNested,
        get: () => internal[x],
      };
    }

    prop.enumerable = true;

    Object.defineProperty(ret, x, prop);
  }

  return ret;
}
const myObject = {
  num: 42,
  str: 'initialValue',
  others: {
    bool: true,
    nestedStr: ''
  }
};

const protectedObject = makeObjectSetterRecursive(myObject, {
  num(x) {
    // Make sure positive
    myObject.num = Math.max(x, 0);
  },
  str(s) {
    // Always double the input
    myObject.str = s + s;
  },
  others: {
    bool(b) {
      // Toggle
      myObject.others.bool = !b
    },
    nestedStr(s) {
      // Add 3 dots
      myObject.others.nestedStr = s + '...'
    }
  }
});

console.log(myObject);
// { num: 42, str: 'initialValue', others: { bool: true, nestedStr: '' } }

protectedObject.num = -1;
protectedObject.str = 'a';
console.log(myObject);
// { num: 0, str: 'aa', others: { bool: true, nestedStr: '' } }

protectedObject.others.bool = true;
protectedObject.others.nestedStr = 'abc';
console.log(myObject);
// { num: 0, str: 'aa', others: { bool: false, nestedStr: 'abc...' } }