First of all, there is a similar question here: what-is-the-syntax-for-typescript-arrow-functions-with-generics
But, I'd like to know the culprit of the syntax error.
I am using an external library, and this is what the definition file (index.d.ts) looks like:
External Library's index.d.ts
declare namespace Student {
export interface Lecture {
lectureName: string;
}
export interface Student {
new (): Student;
on1(eventName: string, callback: (<T>(lecture: T, oldLecture: T) => void) |
((name: string, ...args: any[]) => void)): void;
on2(eventName: string, callback: (<T>(lecture: T, oldLecture: T) => void)): void;
}
}
declare var Student: Student.Student;
declare module "student" {
export = Student;
}
Note that there are two functions: on1 and on2 in Student.Student - the function on1 has a bit more code.
So here are my code examples.
Case 1
import * as Student from 'student';
import { Lecture } from 'student';
export class MyStudent {
student: Student.Student;
constructor() {
this.student = new Student();
this.student.on1('test', (lecture: Lecture, oldLecture: Lecture) => {
// Argument of type error
});
this.student.on2('test', (lecture: Lecture, oldLecture: Lecture) => {
// Argument of type error
});
}
}
The function on1 gives the below error:
Argument of type '(lecture: Lecture, oldLecture: Lecture) => void' is not assignable to parameter of type '((lecture: T, oldLecture: T) => void) | ((name: string, ...args: any[]) => void)'. Type '(lecture: Lecture, oldLecture: Lecture) => void' is not assignable to type '(name: string, ...args: any[]) => void'. Types of parameters 'lecture' and 'name' are incompatible. Type 'string' is not assignable to type 'Lecture'.
The function on2 gives the below error:
Argument of type '(lecture: Lecture, oldLecture: Lecture) => void' is not assignable to parameter of type '(lecture: T, oldLecture: T) => void'. Types of parameters 'lecture' and 'lecture' are incompatible. Type 'T' is not assignable to type 'Lecture'.
I thought this example is the right way to implement the code - but why this gives an error?
Case 2
import * as Student from 'student';
import { Lecture } from 'student';
export class MyStudent {
student: Student.Student;
constructor() {
this.student = new Student();
this.student.on1('test', <Lecture>(lecture: Lecture, oldLecture: Lecture) => {
lecture.lectureName;
// Error: Property 'lectureName' does not exist on type 'Lecture'
});
this.student.on2('test', <Lecture>(lecture: Lecture, oldLecture: Lecture) => {
lecture.lectureName;
// Error: Property 'lectureName' does not exist on type 'Lecture'
});
}
}
In this example, I put <Lecture>
in front of the arrow function - so there is no error in the implementation, but now I cannot use lecture.lectureName
at all. Why?
Case 3
import * as Student from 'student';
import { Lecture } from 'student';
export class MyStudent {
student: Student.Student;
constructor() {
this.student = new Student();
this.student.on1('test', <T extends Lecture>(lecture: T, oldLecture: T) => {
lecture.lectureName; // Yay! No problem!
});
this.student.on2('test', <T extends Lecture>(lecture: T, oldLecture: T) => {
// Argument of type error
});
}
}
So this example has the correct answer - however, the function on2 still gives the argument of type error, just like the case 1's example. Shouldn't it be okay since the function on1 is okay?
Case 4
import * as Student from 'student';
import { Lecture } from 'student';
export class MyStudent {
student: Student.Student;
constructor() {
this.student = new Student();
this.student.on1('test', () => () => (lecture: Lecture, oldLecture: Lecture) => {
lecture.lectureName; // Yay! No error!
});
this.student.on2('test', () => () => (lecture: Lecture, oldLecture: Lecture) => {
lecture.lectureName; // Yay! No error!
});
}
}
I found this solution accidentally - and both functions are working fine. But I have no idea why this is working.
I spent some time trying to figure out the exact cause by looking at these references (because I love TypeScript):
- https://github.com/teppeis/typescript-spec-md/blob/master/en/ch04.md
- https://basarat.gitbooks.io/typescript/content/docs/types/generics.html
- https://github.com/Microsoft/TypeScript/issues/3323
- Specify return type in TypeScript arrow function
but I am still wondering the exact cause of this issue.