skip to Main Content

I have a Javascript object challenge which I cannot translate to Typescript.

var someData = [
    'Some String',
    {'age': '46'}
];


const addressObject = {
    street: 'Main Street',
    number: '14'
}

const resultObject = {
    address: addressObject,
    ...someData[1]
}

console.log(resultObject)

Result is, and should be:

{ address: { street: 'Main Street', number: '14' }, age: '46' }

In TypeScript, I cannot figure out how to get the second element in someData to be spread in the resultObject.

Tried the spread operator in various forms:

...someData[1]  // "Property assignment expected"

[...someData[1]]  // "Expression expected"

Even if I only take the second prop of someData:

const someData_1 = someData[1]
const resultObject = {
  address: addressObject,
  ...someData_1
}

Same error.

Complexity is probably in original someData array which has a string as first element and object as second element, which none of the other TypeScript/spread example/articles have.

2

Answers


  1. Try this:

    interface Address {
        street: string;
        number: string;
    }
    
    interface Age {
        age: string;
    }
    
    type SomeData = [ string, Age ]
    
    var someData: SomeData = [
        'Some String',
        {'age': '46'}
    ];
    
    const addressObject: Address = {
        street: 'Main Street',
        number: '14'
    }
    
    const resultObject = {
        address: addressObject,
        ...someData[1]
    }
    
    console.log(resultObject);
    

    My output:
    npx ts-node src/test.ts

    { address: { street: 'Main Street', number: '14' }, age: '46' }
    
    Login or Signup to reply.
  2. By default TypeScript will infer that an array literal has a plain array type, where each element is of the same type. Your someData is therefore treated like an array where each element might be a string or an {age: string}. The compiler intentionally doesn’t keep track of which types might appear at which indices, because often people would like to change the contents of their arrays, and such information would be impossible to maintain in such a scenario (at least given TypeScript’s type system, in which values types don’t arbitrarily mutate).

    But you don’t want to use someData as an abitrary array of strings-or-age-havers. Instead, you want it to be a constant set of data where the first element is a string and the second is an age-haver and there are no elements at other indices. The inference algorithm, while reasonable for a wide range of real-world code, didn’t do what you wanted. So you need to make a change.


    There are various ways to change the typings here. You could annotate or assert the type of someData.

    But the easiest way to express the intent that someData‘s structure won’t change is to use a const assertion:

    var someData = [
        'Some String',
        { 'age': '46' }
    ] as const;
    

    This still uses TypeScript’s inference algorithm, but you’re giving the compiler a hint that you don’t plan to mutate the data structure and that you do care about all the literal types and their positions. Now the type of someData is

    /* var someData: readonly ["Some String", {
        readonly age: "46";
    }] */
    

    a tuple type where the positions of the various data, including the literal types of their string values, are known.

    And now someData[1] is known to be an {age: "46"}, so you can spread it into a new object without the compiler worrying that you might be spreading a string:

    const resultObject = {
        address: addressObject,
        ...someData[1]
    }
    /* const resultObject: {
        age: "46";
        address: {
            street: string;
            number: string;
        };
    } */
    

    Playground link to code

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search