如何定义这种复杂的打字稿类型?

时间:2018-04-24 04:41:59

标签: typescript

export type Model<S> = {
  state: S,
  setState?: never,
  setStateSync?: never,
} & Record<string, any | S | ((
  this: Model<S> & {
    setState<K extends keyof S>(partialState: S | Pick<S, K> | ((prevState: Readonly<S>) => S | Pick<S, K>), callback?: () => void): void;
    setStateSync<K extends keyof S>(partialState: S | Pick<S, K>): void;
  },
  ...args: any[]
) => any)>    

export function createContext<S, M extends Model<S>>(model: M) {}

我想指定一个参数model类型。

model应该是普通对象,它必须具有属性state: Object,它必须没有属性setStatesetStateSync,它可以具有任何其他属性。

如果属性是函数,则函数this应为this: typeof model & { setState: XXX, setStateSync: XXXX } ...

所以我声明类似上面的类型,但它不起作用:Model<S>createContext都可以,但是调用createContext是不行的。我无法在没有错误通知的情况下将参数传递给createContext

也许这样的代码可以更准确地显示我的意图:

type StateAble<S> = {
    setState(
        partialState: Partial<S> | ((prevState: Readonly<S>) => Partial<S>),
        callback?: () => void
    ): void;
    setStateSync(partialState: Partial<S>): void;
}

type Model<S> = Exclude<
    { state: S } & Record<string, any> &
        Record<string, (this: Model & StateAble<S>) => any>,
    StateAble<S>
>

function createContext<S, M extends Model<S>>(model: M) { }

createContext({
    state: { a: 1 },
    abc: 123,
    def(a: number[]) {
        console.log(a.concat(1))
        console.log(this.abc)
        console.log(this.def)
        console.log(this.state.a);
    }
});

但是打字稿告诉我Type alias 'Model' circularly references itself.

版本3:

type StateAble<S> = {
  setState(
      partialState: Partial<S> | ((prevState: Readonly<S>) => Partial<S>),
      callback?: () => void
  ): void;
  setStateSync(partialState: Partial<S>): void;
}

function createContext<M extends {
  state: Object, setState?: never, setStateSync?: never
} & Record<
    Exclude<keyof M, 'state' | 'setState' | 'setStateSync'>,
    (this: M & StateAble<M['state']>) => void
>>(model: M) {

}

createContext({
  state: { logined: false },
  login() {
    setTimeout(() => {
      this.setState({ logined: true });
    }, 1000);
  },
  logout() {
    this.setState({ logined: false });
  },
});

将其粘贴到TypeScript-Playground上,错误为:Argument of type '{ state: { logined: boolean; }; login(): void; logout(): void; }' is not assignable to parameter of type '{ state: Object; setState?: never; setStateSync?: never; } & Record<never, (this: StateAble<any>)...'. Object literal may only specify known properties, and 'login' does not exist in type '{ state: Object; setState?: never; setStateSync?: never; } & Record<never, (this: StateAble<any>)...'.

不知道为什么Exclude<keyof M, 'state' | 'setState' | 'setStateSync'>成为never

如果我将(this: M & StateAble<M['state']>) => void更改为numbercreateContext({ state: { logined: false }, a: 'some_thing' }),则Exclude<keyof M, 'state' | 'setState' | 'setStateSync'>会变为正确Record<"a", number>

0 个答案:

没有答案