typescript 扩展对象的“this”

9lowa7mx  于 2023-11-20  发布在  TypeScript
关注(0)|答案(2)|浏览(166)

我有一个返回一个对象的函数。稍后,这个对象将被扩展为属性(例如$watch):

export default () => {
  const myObject = {
    property1: 'string1',
    property2: 1,
    init() {
      console.log('property1', this.property1)

      this.$watch('property1', (value) => { 
        console.log('property1 changed', value)
      })
    },
  }

  return myObject
}

个字符
有没有办法将OtherProps的属性注入到myObject中,这样TypeScript就不会说Property '$watch' does not exist on type ...了?
我设法以这种方式输入它,但它对我的口味来说有点过于冗长,因为我必须单独输入定义:

type WithProps<T> = ThisType<T & OtherProps<T>>

type Test = WithProps<{
  property1: string
  property2: number
  init(): void
}>

export default () => {
  const myObject: Test = {
    property1: 'string1',
    property2: 1,
    init() {},
  }

  return myObject
}


我发现this answer似乎工作得很好,但我很难适应我的情况。

liwlm1x9

liwlm1x91#

如果希望Typescript将generic类型参数推断为WithProps,则需要编写一个泛型辅助函数。类型参数推断仅在调用泛型 * 函数 * 时发生;泛型 * 类型 * 没有类似的行为。下面是一种方法:

const withProps = <T,>(wp: T & ThisType<T & OtherProps<T>>) => wp;

字符串
这样,你就不用写const myObj: WithProps<{⋯}> = {⋯}了,你可以写const myObj = withProps({⋯})myObj的类型会自动被推断出来。我不知道你真的需要WithProps类型,所以我只是在上面内联了它。哦,你真的需要T & ThisType<T & OtherProps<T>>而不仅仅是ThisType<T & OtherProps<T>>。没有T &,你会得到由神奇的/内在的ThisType实用程序类型提供的上下文this行为,但是对象的结果类型将与T无关。
好吧,让我们试试:

const x = () => withProps({
  property1: 'string1',
  property2: 1,
  init() {
    console.log('property1', this.property1)
    this.$watch('property1', (value) => {
      console.log('property1 changed', value)
    })
  },
});


看起来不错。init()中的this类型知道属性,也知道$watchx的推断类型是

/* const x: () => {
    property1: string;
    property2: number;
    init(): void;
} & ThisType<{
    property1: string;
    property2: number;
    init(): void;
} & OtherProps<{
    property1: string;
    property2: number;
    init(): void;
}>> */


其行为如指定的:

console.log(x().property1.toUpperCase()) // "STRING1"


Playground链接到代码

pgccezyw

pgccezyw2#

我能想到的最好的方法是使用函数this type来告诉TypeScript this将引用什么:
function myFn<T extends { ... }>(this: T){}
这样做意味着你可以告诉TypesScript this必须有一些现有的属性。
下面是我提出的init函数声明:

type $WatchType =  (property: string, callback: (value: unknown) => void) => void;

function init<
  T extends { property1: string; property2: number; $watch: $WatchType },
>(this: T) {
  console.log("property1", this.property1);

  this.$watch("property1", (value) => {
    console.log("property1 changed", value);
  });
}

字符串
然后,无论实际添加$watch的函数是什么,都应该返回修改了this的新对象:

type $WatchType = (
  property: string,
  callback: (value: unknown) => void,
) => void;

const myObject = {
  property1: "string1",
  property2: 1,
  init<T extends { property1: string; property2: number; $watch: $WatchType }>(
    this: T,
  ) {
    console.log("property1", this.property1);

    this.$watch("property1", (value) => {
      console.log("property1 changed", value);
    });
  },
};

function addWatch<T extends Record<string, unknown>>(
  input: T,
): T & { $watch: $WatchType } {
  return Object.assign(input, {
    $watch: (property: string, callback: (value: unknown) => void) => {},
  });
}

const doesntWork = myObject.init(); // errors because myObject doesn't have correct this
const doesWork = addWatch(myObject).init(); // works because myObject has correct this.


可能还有其他我不知道的魔法方法,但这就是我要做的。

相关问题