vuex 저장소 형식에서 개체 속성을 다이너믹하게 설정하는 방법(유형 안전 유지 중)
나는 (자바스크립트에서 TS로 변환하는 과정에서) 고급 플러그인은 없고 그냥 입력만 한 형태의 Vue 3 프로젝트가 있다.
나는 주문 물체의 다른 성질을 설정하는 돌연변이를 가지고 있기 때문에 모든 성질에 대해 돌연변이를 쓸 필요가 없다.JS에서는 다음과 같이 보인다.
setMetaProp(state, { propName, value }) {
state.order[propName] = value;
}
이제, TS로 물건을 배우고 변환하는 과정에서, 나는 타입 안전으로 이것을 할 수 있는 기능을 작성했다.
export interface PropNameKeyValue<KeyType, ValueType> {
propName: KeyType;
value: ValueType;
}
export declare function setMeta<Key extends keyof Order>(payload: PropNameKeyValue<Key, Order[Key]>): void;
//type of is_quote is boolean in the type declaration of type Order
setMeta({ propName: 'is_quote', value: '123' });
//if I try to set is_quote to something else than boolean here
// tsc catches the problem and complains that this should be a boolean
이것은 굉장하고 제대로 작동하지만, 만약 내가 상점에 가지고 있는 돌연변이에 이것을 적용하려고 한다면, 타입 안전은 더 이상 아니다, 타입 오더에 존재하지 않는 속성을 설정하려고 한다면, 적어도 타입 오더에 속성이 존재하지 않는다는 것을 말해준다.
지금까지 내가 가지고 있는 코드는 다음과 같다. 나는 스크린샷과 함께 IDE에서 각 기능 위에 마우스를 올려 놓을 때 스크린샷을 포함할 것이다(또 :):
// type declaration of mutations
export type Mutations < S = State > = {
[MutationTypes.setMetaProp] < PropName extends keyof Order > (
state: S,
payload: PropNameKeyValue < PropName, Order[PropName] >
): void;
};
// implementation
export const mutations: MutationTree < State > & Mutations = {
[MutationTypes.setMetaProp](state, {
propName,
value
}) { //i have tried not destructuring, did not change the outcome
state.order && (state.order[propName] = value);
},
};
// call the mutation:
commit(MutationTypes.setMetaProp, {
propName: 'is_quote',
value: '123'
}); // no complaints from tsc
//also type declaration of commit
commit < Key extends keyof Mutations, Params extends Parameters < Mutations[Key] > [1] > (
key: Key,
payload: Params
): ReturnType < Mutations[Key] > ;
그래서 결국 제 질문은 어떻게 하면 이 문제를 해결하고 유형 안전을 가질 수 있을까 하는 겁니다.
(어쨌든 내가 여기 있다면, 이것을 바꿀 방법이 있을까? 그러면 동적 유형을 유형 매개변수로 지정할 수 있다(따라서 다른 장소에서 사용할 경우 순서 또는 유형을 입력할 필요가 없음)
업데이트: 플레이그라운드 링크 표시 문제:
업데이트 2: 지연된 솔루션...(SpecialCommit ???
기본적으로 나는 AugmentActionContext에 다른 유형의 돌연변이를 선언했고, 타입 Order에만 작용한다(가장 지연된 솔루션이지만 내가 생각할 수 있는 유일한 솔루션)
업데이트 3:플레이그라운드에 대답하기 위해. 그래서 기본적으로 나는 질문의 경우 내가 하고 싶었던 것뿐만 아니라 다른 종류의 돌연변이를 갖게 될 것이다.당신의 답변은 배경에서 무슨 일이 일어나고 있는지 어느 정도 밝혀주었는데, 나는 그것에 대해 감사하고 있다.다른 생각이 있나?아니면 그냥 내가 내 지체된 해결책에서 했던 것처럼 해야 할까?기본적으로 내 지체된 해결책은 네 것과 비슷해, 나는 그 파라암 파트의 문제가 단지 왜 그것이 작동하지 않는지 몰랐어.하지만 설명해줘서 고마워!또한 이 해결책에 대한 나의 또 다른 문제점은 모든 커밋에 대해 오직 하나의 유형으로 나의 사용을 제한한다는 것이다.아마도 나는 순서 하위 오브젝트의 속성을 설정하기를 원할 것이다. 이것은 또한 그 옵션도 없앨 것이다.
지금 내 새로운 질문은 내가 TypeScript와 Vuex 콤보를 너무 많이 요구하는가 하는 것이다.
어떤 때는 이 모든 제네릭들과 어울리기가 어렵다.
인setMeta
TS함수는 당신이 이 함수를 부르기 때문에 그 유형을 유추할 수 있다.
문제는 다음과 같다.
Parameters<Mutations[Key]>[1]
적어도 이 경우에는 기능을 호출하지 않고는 유하중을 유추할 수 없다.TS는 단지 그가 어떤 가치를 기대해야 하는지 모를 뿐이다.
으로 유추할 수는 없다.Parameters
로부터의setMeta
:
export declare function setMeta<Key extends keyof Order>(payload: PropNameKeyValue<Key, Order[Key]>): void;
type O = Parameters<typeof setMeta>[0] // PropNameKeyValue<keyof Order, any>
가장 간단한 해결책은 허용된 모든 값의 조합 유형을 만드는 것이다.
type Values<T> = T[keyof T]
type AllowedValues = Values<{
[Prop in keyof Order]: PropNameKeyValue<Prop, Order[Prop]>
}>
이제 예상대로 작동함:
import {
ActionContext,
ActionTree,
MutationTree,
} from 'vuex';
type Order = {
is_quote: boolean;
order_switches: any;
api_version: string;
order_type: string;
customer_unique_id: string;
crm_customer_id: string;
first_name: string;
last_name: string;
}
type Values<T> = T[keyof T]
type AllowedValues = Values<{
[Prop in keyof Order]: PropNameKeyValue<Prop, Order[Prop]>
}>
export interface PropNameKeyValue<KeyType, ValueType> {
propName: KeyType;
value: ValueType;
}
enum ActionTypes {
DoStuff = "DO_STUFF"
}
enum MutationTypes {
setMetaProp = "SET_META_PROP"
}
export type State = {
order: Order | null;
};
export const state: State = {
order: null,
};
export const mutations: MutationTree<State> & Mutations = {
[MutationTypes.setMetaProp](state, payload) {
state.order && (state.order[payload.propName] = payload.value);
},
};
export type Mutations<S = State> = {
[MutationTypes.setMetaProp]<PropName extends keyof Order>(
state: S,
payload: PropNameKeyValue<PropName, Order[PropName]>
): void;
};
export const actions: ActionTree<State, State> & Actions = {
[ActionTypes.DoStuff]({ commit }) {
commit(MutationTypes.setMetaProp, { propName: 'is_quote', value: true });
commit(MutationTypes.setMetaProp, { propName: 'is_quote', value: 1 }); // expected error
},
}
interface Actions {
[ActionTypes.DoStuff](context: AugmentedActionContext): void;
}
type AugmentedActionContext = {
commit<Key extends keyof Mutations>(
key: Key,
payload: AllowedValues // change is here
): ReturnType<Mutations[Key]>;
} & Omit<ActionContext<State, State>, 'commit' | 'getters' | 'dispatch'>;
갱신하다
과부하했다.commit
기능을 발휘하다
상태 :: 관련: 잘TS는 일반적으로 돌연변이를 잘 다루지 못한다.물체는 열쇠와 어긋나기 때문에string|boolean
에 평가되다never
왜냐하면string & boolean = never
.
변형에 대한 내 글을 타이프로 봐줘.
import {
ActionContext,
ActionTree,
MutationTree,
} from 'vuex';
type Order = {
is_quote: boolean;
// order_switches: any;
api_version: string;
order_type: string;
customer_unique_id: string;
crm_customer_id: string;
first_name: string;
last_name: string;
}
type Artwork = {
artwork_id: number;
artwork_size: string;
}
type Values<T> = T[keyof T]
type AllowedValues<Type> = Values<{
[Prop in keyof Type]: {
propName: Prop;
value: Type[Prop];
}
}>
export interface PropNameKeyValue<KeyType, ValueType> {
propName: KeyType;
value: ValueType;
}
enum ActionTypes {
DoStuff = "DO_STUFF"
}
enum MutationTypes {
setMetaProp = "SET_META_PROP",
setArtworkProp = "SET_ARTWORK_PROP",
setRandomVar = "SET_RANDOM_VAR",
}
export type State = {
order: Order | null;
artwork: Artwork | null;
randomVar: string;
};
export const state: State = {
order: null,
artwork: null,
randomVar: '',
};
function setMeta<S extends Values<State>, Key extends keyof S>(state: S, payload: AllowedValues<S>) { }
export const mutations: MutationTree<State> & Mutations = {
[MutationTypes.setMetaProp]: (state, payload) => {
const x = state.order as Order
if (state.order) {
// TS is unsure about safety
const q = state.order[payload.propName] // string|boolean
const w = payload.value // string | boolean"
/**
* Because both
* state.order[payload.propName] and payload.value
* evaluated to stirng | boolean
* TS thinks it is not type safe operation
*
* Pls, keep in mind, objects are contravariant in their key types
*/
// workaround
Object.assign(state.order, { [payload.propName]: payload.value })
}
if (payload.propName === 'api_version') {
state.order && (state.order[payload.propName] = payload.value);
}
state.order && (state.order[payload.propName] = payload.value);
},
[MutationTypes.setArtworkProp](state, payload) {
state.artwork && (state.artwork[payload.propName] = payload.value);
},
[MutationTypes.setRandomVar](state, payload) {
state.randomVar = payload;
},
};
export type Mutations<S = State> = {
[MutationTypes.setMetaProp]: (
state: S,
payload: AllowedValues<Order>
) => void;
[MutationTypes.setArtworkProp]: (
state: S,
payload: AllowedValues<Artwork>
) => void;
[MutationTypes.setRandomVar]: (
state: S,
payload: string
) => void; //added these mutations: setArtworkProp and setRandomVar, they will be unusable from now on...
};
export const actions: ActionTree<State, State> & Actions = {
[ActionTypes.DoStuff]({ commit }) {
commit(MutationTypes.setMetaProp, { propName: 'is_quote', value: true });
commit(MutationTypes.setArtworkProp, { propName: 'artwork_id', value: 2 });
commit(MutationTypes.setArtworkProp, { propName: 'artwork_id', value: '2' });// expected error
commit(MutationTypes.setRandomVar, '2');
commit(MutationTypes.setRandomVar, 2); // expected error
},
}
interface Actions {
[ActionTypes.DoStuff](context: AugmentedActionContext): void;
}
// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
type Overloading = UnionToIntersection<Values<{
[Prop in keyof Mutations]: {
commit(
key: Prop,
payload: Parameters<Mutations[Prop]>[1]
): ReturnType<Mutations[Prop]>
}
}>>
type AugmentedActionContext = Overloading & Omit<ActionContext<State, State>, 'commit' | 'getters' | 'dispatch'>;
'Programing' 카테고리의 다른 글
Vue-roouter 및 Vuex와 함께 작동하도록 경로 매개 변수를 가져오는 방법 (0) | 2022.05.03 |
---|---|
Bootstrap-Vue 페이지 지정이 자동으로 재설정되지 않도록 하는 방법 (0) | 2022.05.03 |
vuex 입력을 편집하기 위해 vuex 개체를 바인딩하는 깨끗한 방법 (0) | 2022.05.03 |
네임스페이스(C) (0) | 2022.05.02 |
META-INF의 목적은 무엇인가? (0) | 2022.05.02 |