2

I'm porting some code to TypeScript, and a bit stumped by this error. The SomeObject type is supposed to allow an object with any named keys which equate to string values. When using in unpacked argument fields, I get two errors:

'SomeObject' is declared but its value is never read.

and

Cannot find name 'someArg'. Did you mean the instance member 'this.someArg'?

Here's the code:

type SomeObject = { [key: string]: string; }

class SomeClass {

  someArg: SomeObject

  constructor ({someArg: SomeObject} = {}) {
    this.someArg = someArg
  }
}

module.exports = SomeClass;

Here you can see where TypeScript has a problem in VS Code:

enter image description here

Am I missing something? I would expect to be able to create an instance of this class like so:

new SomeClass({someArg: {hello: 'world'}}) // Passes TypeScript

new SomeClass({someArg: {hello: 1}}) // Fails TypeScript as value is not a string
2
  • {someArg: SomeObject} = {} - this doesn't looks right to me. Is this destructuring or are you specifying type for someArg? My guess is that this ambiguity is causing the issue. Commented Jul 23, 2021 at 11:09
  • This is destructuring or "argument unpacking" in JS. Perhaps this is confusing TypeScript but there is a lot of this in the codebase that I'm porting so I need a way of converting appropriately. Commented Jul 23, 2021 at 11:32

2 Answers 2

2

You need to declare your type in this way:

type SomeObject = { [key: string]: string; }

class SomeClass {

  someArg: SomeObject

  constructor({ someArg }: { [key: string]: SomeObject } = {}) {
    this.someArg = someArg
  }
}

Since someArg is optional, you should use indexed type, or you can use Record<string, SomeObject> instead of { [key: string]: SomeObject }

UPDATE

type SomeObject = { [key: string]: string; }

class SomeClass {

    someArg: SomeObject | undefined

    constructor({ someArg }: { someArg?: SomeObject } & Record<PropertyKey, any> = {}) {
        this.someArg = someArg
    }
}

const x = new SomeClass({}) // ok
const y = new SomeClass({ age: 42 }).someArg // ok
const z = new SomeClass({ someArg: { age: '42' } }).someArg // ok

More about destructuring in Typescript you can find in this article

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

3 Comments

Surely the above would mean that any property I added to the incoming argument must also be of type SomeObject. This is no good if I need different types from differing destructured properties.
Ok, so I think reading about it here it seems that, because {foo: bar} is actually a valid syntax in JavaScript, TypeScript must be much more verbose when dealing with destrucutring assignment. Your answer is good but it might be worth linking the article. Will mark as correct.
@shennan added link to the answer
0

Remove you {} and use...

type SomeObject = { [key: string]: string; }

class SomeClass {

  someArg: SomeObject

  constructor (someArg: SomeObject = {}) {
    this.someArg = someArg
  }
}

class fred {

    fredJnr:SomeClass = new SomeClass( { Hello: "fred"})
}
module.exports = SomeClass;

Does that work for your use case?

1 Comment

No, someArg is a property of an object passed to SomeClass. Initialising signature should be new SomeClass({someArg: { Hello: "fred"}})

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.