所需:对于传递给createStore
函数的数组中的每个元素,selector
的第二种类型应与value
的类型匹配。
Ex:如果selector
属性的类型为Selector<boolean, number>
,则value
属性的类型应该为number
,而与数组其他元素的类型无关。
export type Selector<S, Result> = (state: S) => Result;
export interface SelectorWithValue<S, Result> {
selector: Selector<S, Result>;
value: Result;
}
export interface Config<T, S, Result> {
initialState?: T;
selectorsWithValue?: SelectorWithValue<S, Result>[];
}
export function createStore<T = any, S = any, Result = any>(
config: Config<T, S, Result> = {}
): Store<T, S, Result> {
return new Store(config.initialState, config.selectorsWithValue);
}
export class Store<T, S, Result> {
constructor(
public initialState?: T,
public selectorsWithValue?: SelectorWithValue<S, Result>[]
) {}
}
const selectBooleanFromString: Selector<string, boolean> = (str) => str === 'true';
const selectNumberFromBoolean: Selector<boolean, number> = (bool) => bool ? 1 : 0;
createStore({
selectorsWithValue: [
{ selector: selectBooleanFromString, value: false },
{ selector: selectNumberFromBoolean, value: 'string' } // should error since isn't a number
],
});
这是我第一次尝试修改Typescript游乐场@jcalz provided for the nested array use case:
说明:以上是我对数组第二个元素强制执行错误的尝试。但是,它确实出错,但是原因有误。这是我最初的尝试,根本不给出任何错误:
export type Selector<S, Result> = (state: S) => Result;
export interface SelectorWithValue<S, Result> {
selector: Selector<S, Result>;
value: Result;
}
export interface Config<T> {
initialState?: T;
selectorsWithValue?: SelectorWithValue<any, any>[];
}
export function createStore<T = any>(
config: Config<T> = {}
): Store<T> {
return new Store(config.initialState, config.selectorsWithValue);
}
export class Store<T> {
constructor(
public initialState?: T,
public selectorsWithValue?: SelectorWithValue<any, any>[]
) {}
}
const selectBooleanFromString: Selector<string, boolean> = (str) => str === 'true';
const selectNumberFromBoolean: Selector<boolean, number> = (bool) => bool ? 1 : 0;
createStore({
selectorsWithValue: [
{ selector: selectBooleanFromString, value: false },
{ selector: selectNumberFromBoolean, value: 'string' } // should error unless value is a number.
//the passed `selector` property is type Selector<boolean, number>, therefor, the `value` should be a number
//the second type of the selector property should match the type of value
],
});
答案 0 :(得分:0)
呃,这很累。我不知道这样做是值得的。存在类型是解决此问题的“正确”解决方案,但是在TS中对它们进行编码的方式将要求您将类型更改为更像诺言的模型(在这里,您可以使用一个函数来代替类型为T
的值)作用在T
上的回调。
无论如何,这部分没有改变:
// unchanged:
export type Selector<S, Result> = (state: S) => Result;
export interface SelectorWithValue<S, Result> {
selector: Selector<S, Result>;
value: Result;
}
export class Store<T, S, Result> {
constructor(
public initialState?: T,
public selectorsWithValue?: SelectorWithValue<S, Result>[]
) { }
}
这部分确实发生了变化:
// changed:
export interface Config<T, S, Result> {
initialState?: T;
// changed below, prefer inferring tuples over just arrays
selectorsWithValue?: SelectorWithValue<S, Result>[] | [SelectorWithValue<S, Result>];
}
// drill down into a Config, and make sure value R is assignable to the R in Selector<S, R>
type ConfigOkay<C extends Config<any, any, any>> =
C extends { selectorsWithValue?: infer A } ?
A extends SelectorWithValue<any, any>[] ?
{
selectorsWithValue?: { [I in keyof A]: A[I] extends {
selector: Selector<infer S, infer R1>, value: infer R2
} ? { selector: Selector<S, R1>, value: R1 } : never }
} : never : never;
export function createStore<C extends Config<T, S, any>, T = any, S = any>(
config: C & ConfigOkay<C> = {} as any // assertion:
// default value {} is not seen as C & ConfigOkay<C> I guess
): Store<T, S, any> {
return new Store(config.initialState, config.selectorsWithValue);
}
这里就在起作用。
const selectBooleanFromString: Selector<string, boolean> = (str) => str === 'true';
const selectNumberFromBoolean: Selector<boolean, number> = (bool) => bool ? 1 : 0;
createStore({
selectorsWithValue: [
{ selector: selectBooleanFromString, value: false },
{ selector: selectNumberFromBoolean, value: 1 } // okay
],
});
createStore({
selectorsWithValue: [
{ selector: selectBooleanFromString, value: false },
{ selector: selectNumberFromBoolean, value: "string" } // error!
],
});
是吗?它是如此复杂,以至于您可能想将其推入一个没有凡人需要看的图书馆中……
...或者您可能想要重构其他内容,说一些将SelectorWithValue
品牌为“好”的话,然后只接受“好”的话:
export type Selector<S, Result> = (state: S) => Result;
export interface SelectorWithValue<S, Result> {
selector: Selector<S, Result>;
value: Result;
}
export interface GoodSelectorWithValue<S> {
knownGood: true
selector: Selector<S, any>;
value: any
}
function vetSV<S, R>(x: SelectorWithValue<S, R>): GoodSelectorWithValue<S> {
return Object.assign({ knownGood: true as true }, x);
}
export interface Config<T, S> {
initialState?: T;
selectorsWithValue?: GoodSelectorWithValue<S>[];
}
export function createStore<T = any, S = any>(
config: Config<T, S> = {}
): Store<T, S> {
return new Store(config.initialState, config.selectorsWithValue);
}
export class Store<T, S> {
constructor(
public initialState?: T,
public selectorsWithValue?: GoodSelectorWithValue<S>[]
) { }
}
const selectBooleanFromString: Selector<string, boolean> = (str) => str === 'true';
const selectNumberFromBoolean: Selector<boolean, number> = (bool) => bool ? 1 : 0;
createStore<any, any>({
selectorsWithValue: [
vetSV({ selector: selectBooleanFromString, value: false }),
vetSV({ selector: selectNumberFromBoolean, value: 1 })
]
}); // okay
createStore<any, any>({
selectorsWithValue: [
vetSV({ selector: selectBooleanFromString, value: false }),
vetSV({ selector: selectNumberFromBoolean, value: "string" }) // error!
]
});
这可能更好,在这种情况下,您需要人们做更多的工作来创建SelectorWithValue
。请注意,我必须在<any, any>
上指定createStore()
...这是因为它期望S
是像string
或boolean
这样的单一事物,而不是{ {1}},这是必须的。因此,您可能需要在那里进行一些重构,以准确指定要限制string | boolean
的内容。
希望有所帮助;再次祝你好运。