여러개의 CodeEditor들이 개별적으로 동작하게 만드는 어플리케이션을 구현했다.
각 Editor들이 개별적으로 동작하게 만들기 위해서는 식별할 수 있는 각각의 id입력이 필수였기 때문에, 어떻게 동적으로 키를 입력받아야 하는지 로직을 구현하는 부분이 어려웠다.
먼저 id를 받기 위해서는 타입 지정에 [key:string]
을 필수로 입력해야겠다고 생각하고 코드를 작성했다.
Property is not assignable to 'string' index type
이번 오류는 interface 속성에 잘못된 index signiture가 할당되었을 때 발생했다.
예를들어, dispatch의 결과에 따라서 동적인 값을 받는 프로퍼티와 정적인 값을 받는 프로퍼티를 동시에 사용하려 할 때 발생하는 Error다.
interface BundleState {
[key:string]: {
code: string;
err: string;
}
loading: boolean;
}
const initialState: BundleState = {
loading:boolean
};
// Type 'boolean' is not assignable to type '{ code: string; err: string; }'.
loading또한 [key:string]:{code:string, err:string}
의 형식에 맞게 사용하라는 Error같았다.
interface BundleState {
[key:string]: {
loading: boolean;
code: string;
err: string;
}
}
const initialState: BundleState = {};
생각해보니, loading을 따로 바깥으로 뺼 필요 없이, 각각의 Code Editor의 로딩 상황을 의미할 수 있게 동적 프로퍼티 내부에 입력하는게 더 알맞는 것 같아 수정하였다.
이제 action creator를 받아, 각 CodeEditor의 입력된 data를 뿌려줘야 하는데, extraReducer에서의 action을 payload로 받는 것이 불가능하고, meta로 받아서 처리할 수 있었다.
먼저 createAsyncThunk
를 작성하고,
export const asyncBundleThunk = createAsyncThunk<
any,
BundleCompleteAction,
{ rejectValue: Error }
>('bundle/start', async (action: BundleCompleteAction) => {
const { id } = action;
const result = await codeProcessor(action.code);
const data = {
id,
code: result.code,
err: result.err,
};
return data;
});
extraReducer
를 작성했다.
extraReducers: (builder) => {
builder.addCase(asyncBundleThunk.pending, (state, action):BundleState => {
state[action.meta.arg.id] = {
loading: true,
code: '',
err: ''
}
return state
});
builder.addCase(asyncBundleThunk.fulfilled, (state, action:PayloadAction<BundleCompleteAction>):BundleState => {
state[action.payload.id] = {
loading:false,
code: action.payload.code,
err: action.payload.err
}
return state
});
builder.addCase(asyncBundleThunk.rejected, (state, action):BundleState => {
if(action.error.message){
state[action.meta.arg.id] = {
loading:false,
code: '',
err: action.error.message
}
}else{
state[action.meta.arg.id] = {
loading:false,
code: '',
err: 'unkwown error occured'
}
}
return state
});
},
위 코드에서 state[action.meta.arg.id]
는 BundleState의 [key:string]
을 의미한다.
action.meta.arg.id
를 통해서 각 id에 맞는 loading,code,err
프로퍼티를 가질 수 있게 되는 것이다.
rejected 처리에서 error.message
는 string | undefined
이다.
BundleState
의 err
의 속성은 string
만 받을 수 있기 때문에 error.message
의 존재 여부에 따라 err
에 각기 다른 string
타입이 입력 될 수 있게 narrowing 하였다.