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
,它必须没有属性setState
和setStateSync
,它可以具有任何其他属性。
如果属性是函数,则函数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
更改为number
和createContext({ state: { logined: false }, a: 'some_thing' })
,则Exclude<keyof M, 'state' | 'setState' | 'setStateSync'>
会变为正确Record<"a", number>
。