0

I wrote a code which map QueryArrayResult to object in deno using typescript.

const mapDbResultToModelArray = <T>(dbResult: QueryArrayResult<Array<string>>): Array<T> => {
    const objArr: Array<T> = [];
    for (let i = 0; i < (dbResult.rowCount || 0); i++) {
        const obj = {} as T;
        for (let columnInfo of (dbResult.rowDescription || {columns:[]}).columns) {
            //@ts-ignore
            obj[columnInfo.name] = dbResult.rows[i][columnInfo.index - 1];
        }
        objArr[i] = obj;
    }
    return objArr;
}

But this code has //@ts-ignore and problem which I can't expect the type of return obj is T. The Object can have null value

How to assign safely a value with the dynamic property name like columnInfo.name without //@ts-ignore

4
  • 1
    Why even bother using a generic? This code is appears coupled to a specific query result? Just create a specific concrete type and use it. Why do you parameterize QueryResultArray with any and then ask about safety? I'm trying and failing to wrap my head around what you're after here... Commented Mar 18, 2021 at 2:08
  • umm... I just saw a post which assign the value to his object with Literal Types. So, I wrote the function using general. Lastly, I used QueryArrayResult<any> becuase I just copied sorry. I'll change to string to any. Commented Mar 18, 2021 at 2:30
  • Additionally, I just want to know assigning some value to object properties when the name of property is dynamic. Commented Mar 18, 2021 at 2:34
  • 1
    Duplicate of stackoverflow.com/questions/12710905/… Commented Mar 18, 2021 at 11:29

1 Answer 1

1

If you simply want to bypass the ts-ignore here are a few things you can do

  1. use a function that bypasses type checking
for (let columnInfo of (dbResult.rowDescription || {columns:[]}).columns) {
   Object.assign(obj, {[columnInfo.name] : dbResult.rows[i][columnInfo.index - 1]});
}

  1. cast as keyof T
for (let columnInfo of (dbResult.rowDescription || {columns:[]}).columns) {
   obj[columnInfo.name as keyof T] = dbResult.rows[i][columnInfo.index - 1];
}
  1. since you casted the array as Array<T>, you don't need to cast obj as T
 const obj: any = {};
 for (let columnInfo of (dbResult.rowDescription || {columns:[]}).columns) {
   obj[columnInfo.name] = dbResult.rows[i][columnInfo.index - 1];
 }
 objArr[i] = obj;

To explain what is happening... Current there are no restrictions on T, saying <T> T can be a string, number, object, Symbol.. etc.. you can add restrictions on T by using extend ie. <T extends object> would ensure that T would descend from type object. Note: this would not fix your issue. extends is not the same as equals. ie.. In your original function doing

type T = Record<string, any>
const obj = {} as T;

would solve your issues aswell. However doing

const mapDbResultToModelArray = <T extends Record<string, any>>(dbResult: QueryArrayResult<Array<string>>): Array<T> => {
    const objArr: Array<T> = [];
    for (let i = 0; i < (dbResult.rowCount || 0); i++) {
        const obj = {} as T;

would not. You would still have an indexing error.. That is because in the former exampe T really is Record<string, any> but in the ladder example T only inherits from Record<string, any> but could still be of a more specific type ie. T = Record<'key1', 'key2', any>

this is why casting obj[columnInfo.name as keyof T] will always work because if the keys are string then keyof T will be string but if the keys are 'key1' | 'key2' then keyof T will be key1' | 'key2

So even if you typed it out better you could still have issues but hope you would understand them better.

When I got started with TS things like this did seem very annoying, but over time usually investigating issues like this helps you understand the code more deeply. ie. even though since you wrote the code and you know what is happening, this will likely be correct at run time, but ts will highlight what could possibly go wrong if minor mistakes are made.

So do not be discouraged from asking to get a deeper understanding.

Sign up to request clarification or add additional context in comments.

6 Comments

Thank you. It is the solution I want to know. Can you explain why there is an error which is Type 'string' is not assignable to type 'T[keyof T]' in the first but not in the second?
@yeop because a string could be anything, whereas the keys of a typed object are a specific set of strings.
This answer completely abandons type safety. It "fixes" the problem in the same way that ripping a seat belt out of a car "fixes" the problem of you not being able to get it buckled. It may be the best way if OP is very certain that query result isn't going to change, but you may want to at least mention it's not type safe. See for example the linked duplicate in the comments on the question.
@Jared Smith agreed, but sometimes it's best to help someone get one step further such that he gets an understanding over time vs him breaking his computer in two and putting the keyboard throu the monintor
@lonewarrior556 totally fair, and I didn't downvote your answer or anything, I just think it's at least worth mentioning that this is Not The Way You Do It TM.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.