#override readonly string property without error?

10 messages · Page 1 of 1 (latest)

tacit cedar
#

Suppose we have the following. How do we override the value in a subclass, but keep it readonly, without silencing errors on the constructor?

class FooBar {
  static readonly elName = 'foo-bar'
}

class SubClass extends FooBar { // Error 2417: Class static side 'typeof BottomNavigation' incorrectly extends base class static side 'typeof NavigationShell'.
  static readonly elName = 'sub-class'
}

TS playground

If the TypeScript placed the error on elName instead, then I could ignore the error there, which would make sense. But having to silence the error on the class name would potentially silence other errors:

TS Playground

Is there another way to do it? Each subclass should have a readonly unique name, such that it can be used witha know string type, such as

type Foo = {
  [SubClass.elName]: number
}

const obj: Foo = {
  [SubClass.elName] = 123 // ok
}
split saffronBOT
#

@tacit cedar Here's a shortened URL of your playground link! You can remove the full link from your message.

trusktr#0

Preview:```ts
class FooBar {
static readonly elName = "foo-bar"
}

class SubClass extends FooBar {
static readonly elName = "sub-class"
}```

exotic osprey
#

@tacit cedar If the actual name is immaterial in types, then static readonly elName = 'foo-bar' as string

#

Otherwise you do have a type incompatibility

lofty pasture
#

You can also do:

class FooBar {
  static readonly elName: string = 'foo-bar'
}
#

But yes, TS is correct here: SubClass extends FooBar means you should be able to use SubClass in every way you could use FooBar, which is not true if the SubClass.elName is not compatible with FooBar.elName

tacit cedar
#

Wish there was something like static foo: unique string a base class could have, which would allow the subclass to put any string, such that reading it in the base class type checking would just see string, but reading it in the subclass (or subclass user public code) would see the concrete type.

For sake of example (not sure "unique string" is the right semantic):

@element
class CoolEl extends HTMLElement {
  static readonly elementName: unique string = 'cool-el'

  static method() {
    this.elementName // string
  }
}

declare global {
  interface HTMLElementTagNameMap {
    [CoolEl.elementName]: CoolEl // prevent string duplication, avoid re-naming difficulties
  }
}

declare module 'solid-js' {
  namespace JSX {
    interface IntrinsicElements {
      [CoolEl.elementName]: AttrsToPropsSolid<CoolEl, ...> // prevent string duplication, avoid re-naming difficulties
    }
  }
}


declare module 'react' {
  namespace JSX {
    interface IntrinsicElements {
      [CoolEl.elementName]: AttrsToPropsReact<CoolEl, ...> // prevent string duplication, avoid re-naming difficulties
    }
  }
}

and then

@element
class CoolerEl extends CoolEl {
  static readonly elementName: unique string = 'cooler-el'
}

// similar type definitions re-using the CoolerEl.elementName concrete string type
#

There's another way to do it which is to leave the static properties as just string, and map a const elementName for each class to those strings, then re-use the const var in the type definitions.

const elementName = 'cooler-el'

@element
class CoolerEl extends CoolEl {
  static readonly elementName: string = elementName
}

// similar type definitions re-using the CoolerEl.elementName concrete string type
// interface Foo { [elementName]: CoolerEl }, etc
lofty pasture
#

If it's only needed at the type level, I would do something like:

type ElementMap = {
    'cool-el': CoolEl
    // other elements
}

declare global {
    interface HTMLElementTagNameMap extends ElementMap {}
}

declare module 'solid-js' {
    namespace JSX {
        interface IntrinsicElements extends {
            [K in ElementMap]: AttrsToPropsSolid<ElementMap[K], ...>
        } {}
    }
}

// same for react