0

I try to create a class with a string contraint but the it gives an error at the get scale() function.

class Scaling<T extends string> {
  _scale = "";

  constructor(props: T) {
    this._scale = props;
  }

  setScale(scale: T) {
    this._scale = scale;
  }

  get scale(): T {
    return this._scale;
  }

Type 'string' is not assignable to type 'T'. 'string' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string'. }

3 Answers 3

1

You should remove explicit T return type from get scale(): T.

Because during initialization, T inferes as literal type of argument. COnsider this example:

class Scaling<T extends string> {
    _scale = "";

    constructor(props: T) {
        this._scale = props;
    }

    setScale(scale: T) {
        this._scale = scale;
    }

    get scale(): T {
        return this._scale;
    }
}

// T infered as literal "hello"
const result = new Scaling('hello')

Hence, when you want to return T it should be "hello".

In your example, it can't be "hello" because default value of _scale is empty string and accordingly it is infered as a string.

let str = ''

// Type 'string' is not assignable to type '"hello"'
const sameCase = (): 'hello' => str 

You can't use T as an explicit type for _scale because _scale is mutable and types are immutable.

This is why it is unsafe to return T type from get scale

Even if i remove the T from the get scale, i still get an error when for const result = new Scaling("hello"); result.setScale("other")

My bad, did not check it.

In order to make this class generic we need to convert infered T from more specific type to more wider.

type Infer<T> = T extends infer R ? R : never

class Scaling<T extends string> {
  _scale = "";

  constructor(props: Infer<T>) {
    this._scale = props;
  }

  setScale(scale: T) {
    this._scale = scale;
  }

  get scale() {
    return this._scale;
  }
}

// T infered as literal "hello"
const result = new Scaling('hello') // ok

result.setScale('123') // ok

Playground

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

1 Comment

Even if i remove the T from the get scale, i still get an error when for const result = new Scaling("hello"); result.setScale("other");
0

The _scale member should really be of type T. For example:

class Scaling<T extends string> {
  _scale = "" as T;

  constructor(props: T) {
    this._scale = props;
  }

  setScale(scale: T) {
    this._scale = scale;
  }

  get scale(): T {
    return this._scale;
  }
}

4 Comments

In this case you will not be able to call setScale
can you provide an example where it wouldn't work?
const result = new Scaling("hello"); result.setScale("other") // <--- error
that's interesting. it seems that a string literal is not really a string. I've tried some variations, which work: let initial = "hello"; const result = new Scaling(initial); result.setScale("other") or const result = new Scaling("hello" as string); result.setScale("other") But If in the first case I say const initial = "hello" I would get the same error. Looking at the type of let initial vs const initial gives me different result, the fomer is a string, while the latter is "hello", although at runtime typeof returns string in both cases, which is expected.
0

Shouldn't _scale be of type T? So you'd assign with a colon (:)

class Scaling<T extends string> {
  _scale: T;

  constructor(props: T) {
    this._scale = props;
  }

  setScale(scale: T) {
    this._scale = scale;
  }

  get scale(): T {
    return this._scale;
  }
}

The usage would be:

const scaling = new Scaling('Hello World')
console.log(scaling)

Comments

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.