TypeScript compilation fails due to an error related to generics that I can not explain. I have created a minimal example:
interface Processor<T> { process(data:T):void; }
class ArrayProcessor<T> implements Processor<Array<T>|null> { process(data:Array<T>|null):void {} }
// intention (Java syntax, TS?): ... `implements Processor<List<? extends T>/*|null*/>` ...
const applyProcessor = <T,>(processor:Processor<Partial<T>>) => {};
// intention (Java syntax, TS?): ... `<T> void applyProcessor(Processor<? super T> processor)` ...
applyProcessor<Array<number>>(new ArrayProcessor<number>());
// ERROR ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The last line produces an error with this message:
Argument of type 'ArrayProcessor<number>' is not assignable to parameter of type 'Processor<(number | undefined)[]>'.
Types of property 'process' are incompatible.
Type '(data: number[] | null) => void' is not assignable to type '(data: (number | undefined)[]) => void'.
Types of parameters 'data' and 'data' are incompatible.
Type '(number | undefined)[]' is not assignable to type 'number[]'.
Type 'number | undefined' is not assignable to type 'number'.
Type 'undefined' is not assignable to type 'number'.(2345)
I tried to investigate what TypeScript considers assignable. The |null
part might have an influence here:
const myArg1:Processor<Partial<Array<number>|null>> = new ArrayProcessor<number>(); // <- No error
const myArg2:Processor<Partial<Array<number>>> = new ArrayProcessor<number>(); // <- ERROR
// Type 'ArrayProcessor<number>' is not assignable to type 'Processor<(number | undefined)[]>'.(2322)
Why does the applyProcessor
call fail? Where are these undefined
in the error messages coming from? How could the first snippet be fixed?
Bonus question: Does TypeScript have an equivalent to Java's <? super T>
? I would be happy to declare my intention ("... is processor / comparator / consumer / handler / ... of ...") without compilation errors even if the TypeScript compiler can not actually enforce the constraint.
The problem is in Partial
.
When you use Partial
and explicit number
generic argument new ArrayProcessor<number>()
it causes an error because Partial
applies undefined
type. undefined
is not the same as null
.
According to this type definition implements Processor<Array<T> | null
you expect an array or null for Processor
generic while Processor<Partial<T>>
means that you expect smth different.
In order to fix it, you can rewrite applyProcessor
function:
const applyProcessor = <T,>(processor: Processor<T | null>) => { };
OR
const applyProcessor = <T,>(processor: Processor<T>) => { };
Full code:
interface Processor<T> {
process(data: T): void;
}
class ArrayProcessor<T> implements Processor<Array<T> | null> {
process(data: Array<T> | null) { }
}
const applyProcessor = <T,>(processor: Processor<T|null>) => { };
applyProcessor(new ArrayProcessor<number>());