0

How can I make some type optional in Typescript?

I have following code:

const A = <T>(value: T) => new Clazz(value);
const B = <U>(value: U) => new Clazz(undefined, value);

class Clazz<T, U> {
  constructor(private a?: T, private b?: U) {}

  public map<Z>(callback: (value: T) => Z): Clazz<Z, U> {
    return this.a 
      ? A(callback(this.a)) 
      : B(this.b);
  } 
}

But this code fails with error:

Type 'Clazz<Z, {}> | Clazz<undefined, U | undefined>' is not assignable to type 'Clazz<Z, U>'. 
Type 'Clazz<Z, {}>' is not assignable to type 'Clazz<Z, U>'. 
Type '{}' is not assignable to type 'U'.

What is the best way to solve this problem?

My tsconfig.json looks like that:

{
  "compilerOptions": {
    "baseUrl": "",
    "declaration": true,
    "lib": ["es6", "dom"],
    "mapRoot": "./src",
    "module": "es2015",
    "moduleResolution": "node",
    "noEmitHelpers": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "outDir": "./dist",
    "sourceMap": true,
    "strictNullChecks": true,
    "suppressImplicitAnyIndexErrors": true,
    "target": "es2015",
    "typeRoots": [
      "./node_modules/@types"
    ]
  }
}

2 Answers 2

1

The problem is that the return type of the map function does not match the return type of method A because A does not provide the expected contructor parameters for the Clazz class. You can use the same work-around that you use for method B, which is to pass an undefined or null value for the second parameter of the Clazz constructor as so:

const A = <T>(value: T) => new Clazz(value, null);
const B = <U>(value: U) => new Clazz(null, value);

class Clazz<T, U> {
  constructor(private a?: T, private b?: U|null) {}

  public map<Z>(callback: (value: T|undefined) => Z): Clazz<Z|null, U> {
    return this.a 
      ? A(callback(this.a)) 
      : B(this.b);
  } 
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, it did the trick. However, to fulfil the strict requirements of my tsconfig.json (I have added it just now), all generic types (in A and B functions, T and U in Clazz and Z in map) have to extend undefined type. If you update the answer, I'll be able to mark it as correct.
I believe in that case you can use null instead of undefined.
hmm, I've just checked it, and there is some problems with B(this.b) line. It says that Argument of type 'U | undefined' is not assignable to parameter of type 'null'.
0

I must admit I am having a hard time understanding what you are doing there and why - an explanation would be great.

One way to solve your problem would be to parametrize your code like this

const A = <T>(value: T) => new Clazz(value, null);
const B = <U>(value: U) => new Clazz(null, value);

class Clazz<T, U> {
  constructor(private a?: T, private b?: U|null) {}

  public map<Z>(callback: (value: T|undefined) => Z): Clazz<Z|null, U> {
    return this.a 
      ? A(callback(this.a)) 
      : B(this.b);
  } 
}

This compiles but I have a hard time to even express what this map does

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.