Fix typescript definition issues (#1669)

Closes #1667
Closes #1664
Closes #1663
Closes #1659
This commit is contained in:
Samuel Asensi 2019-06-22 13:46:05 +02:00 committed by Jürg Lehni
parent e5d7bafd39
commit b24e9b3835
10 changed files with 17564 additions and 295 deletions

1
dist/paper-full.js vendored
View file

@ -1 +0,0 @@
../src/load.js

17081
dist/paper-full.js vendored Normal file

File diff suppressed because it is too large Load diff

572
dist/paper.d.ts vendored

File diff suppressed because it is too large Load diff

View file

@ -78,7 +78,7 @@ gulp.task('docs:typescript:build', function() {
// ...then generate definition from parsed data... // ...then generate definition from parsed data...
.pipe(shell('node gulp/typescript/typescript-definition-generator.js')) .pipe(shell('node gulp/typescript/typescript-definition-generator.js'))
// ...finally test the definition by compiling a typescript file. // ...finally test the definition by compiling a typescript file.
.pipe(shell('node node_modules/typescript/bin/tsc gulp/typescript/typescript-definition-test.ts')); .pipe(shell('node node_modules/typescript/bin/tsc --project gulp/typescript'));
}); });
// ...finally remove all unneeded temporary files that were used for building. // ...finally remove all unneeded temporary files that were used for building.
gulp.task('docs:typescript:clean:after', function() { gulp.task('docs:typescript:clean:after', function() {

View file

@ -0,0 +1,9 @@
{
"compilerOptions": {
"target": "ES5",
"strictNullChecks": true
},
"files" : [
"typescript-definition-test.ts"
]
}

View file

@ -9,7 +9,6 @@ const mustache = require('mustache');
// Retrieve JSDoc data. // Retrieve JSDoc data.
const data = JSON.parse(fs.readFileSync(__dirname + '/typescript-definition-data.json', 'utf8')); const data = JSON.parse(fs.readFileSync(__dirname + '/typescript-definition-data.json', 'utf8'));
const classes = data.classes; const classes = data.classes;
let globals = data.global.properties;
// Format classes. // Format classes.
classes.forEach(cls => { classes.forEach(cls => {
@ -32,7 +31,7 @@ classes.forEach(cls => {
.filter(filter) .filter(filter)
.map(it => ({ .map(it => ({
name: it._name, name: it._name,
type: formatType(it.type), type: formatType(it.type, { isProperty: true, isSettableProperty: !it.readOnly }),
static: formatStatic(it.isStatic), static: formatStatic(it.isStatic),
readOnly: formatReadOnly(it.readOnly), readOnly: formatReadOnly(it.readOnly),
comment: formatComment(it.comment) comment: formatComment(it.comment)
@ -48,7 +47,7 @@ classes.forEach(cls => {
name: name, name: name,
// Constructors don't need return type. // Constructors don't need return type.
type: !it.isConstructor type: !it.isConstructor
? formatType(getMethodReturnType(it), true) ? formatType(getMethodReturnType(it), { isMethodReturnType: true })
: '', : '',
static: formatStatic(it.isStatic), static: formatStatic(it.isStatic),
// This flag is only used below to filter methods. // This flag is only used below to filter methods.
@ -89,21 +88,25 @@ classes.forEach(cls => {
cls.hasStaticConstructors = cls.staticConstructors.length > 0; cls.hasStaticConstructors = cls.staticConstructors.length > 0;
}); });
// Format global vriables. // PaperScope class needs to be handled slightly differently because it "owns"
globals = globals // all the other classes as properties. Eg. we can do `new paperScope.Path()`.
// Filter global variables that make no sense in type definition. // So we add a `classesPointers` property that the template will use.
.filter(it => !/^on/.test(it._name) && it._name !== 'paper') const paperScopeClass = classes.find(_ => _.className === 'PaperScope');
.map(it => ({ paperScopeClass.classesPointers = classes.filter(_ => _.className !== 'PaperScope').map(_ => ({ name: _.className }));
name: it._name,
type: formatType(it.type), // Since paper.js module is at the same time a PaperScope instance, we need to
comment: formatComment(it.comment) // duplicate PaperScope instance properties and methods in the module scope.
})); // For that, we expose a special variable to the template.
const paperInstance = { ...paperScopeClass };
// We filter static properties and methods for module scope.
paperInstance.properties = paperInstance.properties.filter(_ => !_.static);
paperInstance.methods = paperInstance.methods.filter(_ => !_.static && _.name !== 'constructor');
// Format data trough a mustache template. // Format data trough a mustache template.
// Prepare data for the template. // Prepare data for the template.
const context = { const context = {
paperInstance: paperInstance,
classes: classes, classes: classes,
globals: globals,
version: data.version, version: data.version,
date: data.date, date: data.date,
// {{#doc}} blocks are used in template to automatically generate a JSDoc // {{#doc}} blocks are used in template to automatically generate a JSDoc
@ -130,48 +133,69 @@ function formatStatic(isStatic) {
return isStatic ? 'static ' : null; return isStatic ? 'static ' : null;
} }
function formatType(type, isMethodReturnType, staticConstructorClass) { function formatType(type, options) {
return ': ' + parseType(type, isMethodReturnType, staticConstructorClass); return ': ' + parseType(type, options);
} }
function parseType(type, isMethodReturnType, staticConstructorClass) { function parseType(type, options) {
// Always return a type even if input type is empty. In that case, return // Always return a type even if input type is empty. In that case, return
// `void` for method return type and `any` for the rest. // `void` for method return type and `any` for the rest.
if (!type) { if (!type) {
return isMethodReturnType ? 'void' : 'any'; return options.isMethodReturnType ? 'void' : 'any';
}
if (type === '*') {
return 'any';
} }
// Prefer `any[]` over `Array<any>` to be more consistent with other types. // Prefer `any[]` over `Array<any>` to be more consistent with other types.
if (type === 'Array') { if (type === 'Array') {
return 'any[]'; return 'any[]';
} }
// Handle any type: `*` => `any`
type = type.replace('*', 'any');
// Check if type is a "rest" type (meaning that an infinite number of
// parameter of this type can be passed). In that case, we need to remove
// `...` prefix and add `[]` as a suffix:
// - `...Type` => `Type[]`
// - `...(TypeA|TypeB)` => `(TypeA|TypeB)[]`
const isRestType = type.startsWith('...');
if (isRestType) {
type = type.replace(/^\.\.\./, '');
}
// Handle multiple types possibility by splitting on `|` then re-joining // Handle multiple types possibility by splitting on `|` then re-joining
// back parsed types. // back parsed types.
return type.split('|').map(type => { type = type.split('|').map(splittedType => {
// Handle rest parameter pattern: `...Type` => `Type[]`
const matches = type.match(/^\.\.\.(.+)$/);
if (matches) {
return parseType(matches[1]) + '[]';
}
// Get type without array suffix `[]` for easier matching. // Get type without array suffix `[]` for easier matching.
const singleType = type.replace(/(\[\])+$/, ''); const singleType = splittedType.replace(/(\[\])+$/, '');
// Handle eventual type conflict in static constructors block. For // Handle eventual type conflict in static constructors block. For
// example, in `Path.Rectangle(rectangle: Rectangle)` method, // example, in `Path.Rectangle(rectangle: Rectangle)` method,
// `rectangle` parameter type must be mapped to `paper.Rectangle` as it // `rectangle` parameter type must be mapped to `paper.Rectangle` as it
// is declared inside a `Path` namespace and would otherwise be wrongly // is declared inside a `Path` namespace and would otherwise be wrongly
// assumed as being the type of `Path.Rectangle` class. // assumed as being the type of `Path.Rectangle` class.
if (staticConstructorClass && staticConstructorClass.methods.find(it => it.isStatic && it.isConstructor && formatMethodName(it._name) === singleType) if (options.staticConstructorClass && options.staticConstructorClass.methods.find(it => it.isStatic && it.isConstructor && formatMethodName(it._name) === singleType)
) { ) {
return 'paper.' + type; return 'paper.' + splittedType;
} }
// Convert primitive types to their lowercase equivalent to suit // Convert primitive types to their lowercase equivalent to suit
// typescript best practices. // typescript best practices.
return ['Number', 'String', 'Boolean', 'Object'].indexOf(singleType) >= 0 if (['Number', 'String', 'Boolean', 'Object'].indexOf(singleType) >= 0) {
? type.toLowerCase() splittedType = splittedType.toLowerCase();
: type; }
// Properties `object` type need to be turned into `any` to avoid
// errors when reading object properties. Eg. if `property` is of type
// `object`, `property.key` access is forbidden.
if (options.isProperty && splittedType === 'object') {
return 'any';
}
return splittedType;
}).join(' | '); }).join(' | ');
if (isRestType) {
type += '[]';
}
// We declare settable properties as nullable to be compatible with
// TypeScript `strictNullChecks` option (#1664).
if (options.isSettableProperty && type !== 'any') {
type += ' | null';
}
return type;
} }
function formatMethodName(methodName) { function formatMethodName(methodName) {
@ -195,7 +219,7 @@ function formatParameter(param, staticConstructorClass) {
if (param.isOptional) { if (param.isOptional) {
content += '?'; content += '?';
} }
content += formatType(param.type, false, staticConstructorClass); content += formatType(param.type, { staticConstructorClass });
return content; return content;
} }
@ -306,11 +330,9 @@ function sortMethods(methodA, methodB) {
if (methodB.params === 'object: object') { if (methodB.params === 'object: object') {
return -1; return -1;
} }
} } else if (aIsContructor) {
else if (aIsContructor) {
return -1; return -1;
} } else if (bIsContructor) {
else if (bIsContructor) {
return 1; return 1;
} }
return 0; return 0;

View file

@ -15,11 +15,19 @@
*/ */
declare module paper { declare module paper {
{{#globals}} {{#paperInstance}}
{{#properties}}
{{#doc}}4{{/doc}} {{#doc}}4{{/doc}}
let {{name}}{{type}} let {{name}}{{type}}
{{/globals}} {{/properties}}
{{#methods}}
{{#doc}}4{{/doc}}
function {{name}}({{params}}){{type}}
{{/methods}}
{{/paperInstance}}
{{#classes}} {{#classes}}
@ -30,6 +38,9 @@ declare module paper {
{{static}}{{readOnly}}{{name}}{{type}} {{static}}{{readOnly}}{{name}}{{type}}
{{/properties}} {{/properties}}
{{#classesPointers}}
{{name}}: typeof {{name}}
{{/classesPointers}}
{{#methods}} {{#methods}}
{{#doc}}8{{/doc}} {{#doc}}8{{/doc}}

View file

@ -13,17 +13,6 @@
import * as paper from 'paper'; import * as paper from 'paper';
//
// Global
//
paper.project;
paper.projects;
paper.view;
paper.tool;
paper.tools;
// //
// Utility variables // Utility variables
// //
@ -368,6 +357,7 @@ item.strokeScaling;
item.dashArray; item.dashArray;
item.miterLimit; item.miterLimit;
item.fillColor; item.fillColor;
item.fillColor && item.fillColor.red;
item.fillRule; item.fillRule;
item.shadowColor; item.shadowColor;
item.shadowBlur; item.shadowBlur;
@ -538,6 +528,8 @@ raster.source;
raster.crossOrigin; raster.crossOrigin;
raster.smoothing; raster.smoothing;
raster.onLoad; raster.onLoad;
raster.onLoad = () => {};
raster.onLoad = null;
raster.onError; raster.onError;
raster.getSubCanvas(rectangle); raster.getSubCanvas(rectangle);
raster.getSubRaster(rectangle); raster.getSubRaster(rectangle);
@ -662,6 +654,9 @@ path.length;
path.area; path.area;
path.fullySelected; path.fullySelected;
path.add(segment); path.add(segment);
path.add(point);
path.add([0,0]);
path.add(segment, point, [0,0]);
path.insert(0, segment); path.insert(0, segment);
path.addSegments([ segment ]); path.addSegments([ segment ]);
path.insertSegments(0, [ segment ]); path.insertSegments(0, [ segment ]);
@ -1048,6 +1043,7 @@ view.responds('');
event.timeStamp; event.timeStamp;
event.modifiers; event.modifiers;
event.modifiers.shift;
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
event.stop(); event.stop();
@ -1128,6 +1124,7 @@ keyEvent.toString();
new paper.PaperScope(); new paper.PaperScope();
paperScope.version; paperScope.version;
paperScope.settings; paperScope.settings;
paperScope.settings = null;
paperScope.project; paperScope.project;
paperScope.projects; paperScope.projects;
paperScope.view; paperScope.view;
@ -1141,6 +1138,60 @@ paperScope.setup({} as HTMLCanvasElement);
paperScope.setup(size); paperScope.setup(size);
paperScope.activate(); paperScope.activate();
paper.PaperScope.get(0); paper.PaperScope.get(0);
new paperScope.Color('');
new paperScope.CompoundPath('');
new paperScope.Curve(segment, segment);
new paperScope.CurveLocation(curve, 0);
new paperScope.Event();
new paperScope.Gradient();
new paperScope.GradientStop();
new paperScope.Group();
new paperScope.HitResult();
new paperScope.Item();
new paperScope.Key();
new paperScope.KeyEvent();
new paperScope.Layer();
new paperScope.Matrix();
new paperScope.MouseEvent();
new paperScope.PaperScript();
new paperScope.Path();
new paperScope.PathItem();
new paperScope.Point(0, 0);
new paperScope.PointText(point);
new paperScope.Project(size);
new paperScope.Raster();
new paperScope.Rectangle(point, size);
new paperScope.Segment();
new paperScope.Shape();
new paperScope.Size(0,0);
new paperScope.Style(object);
new paperScope.SymbolDefinition(item);
new paperScope.SymbolItem(symbolDefinition);
new paperScope.TextItem();
new paperScope.Tool();
new paperScope.ToolEvent();
new paperScope.Tween(object, object, object, 0);
new paperScope.View();
//
// Global PaperScope instance
//
paper.version;
paper.settings;
paper.project;
paper.projects;
paper.view;
paper.tool;
paper.tools;
paper.execute('');
paper.execute('', object);
paper.install(object);
paper.setup('');
paper.setup({} as HTMLCanvasElement);
paper.setup(size);
paper.activate();
// //

View file

@ -112,6 +112,7 @@ var PaperScope = Base.extend(/** @lends PaperScope# */{
* The version of Paper.js, as a string. * The version of Paper.js, as a string.
* *
* @type String * @type String
* @readonly
*/ */
version: /*#=*/__options.version, version: /*#=*/__options.version,

View file

@ -63,6 +63,7 @@
* *
* @name view * @name view
* @type View * @type View
* @readonly
*/ */
/** /**

View file

@ -483,9 +483,11 @@ var Path = PathItem.extend(/** @lends Path# */{
* Adds one or more segments to the end of the {@link #segments} array of * Adds one or more segments to the end of the {@link #segments} array of
* this path. * this path.
* *
* @param {Segment|Point} segment the segment or point to be added. * @param {...(Segment|Point|Number[])} segment the segment or point to be
* @return {Segment} the added segment. This is not necessarily the same * added.
* object, e.g. if the segment to be added already belongs to another path * @return {Segment|Segment[]} the added segment(s). This is not necessarily
* the same object, e.g. if the segment to be added already belongs to
* another path.
* *
* @example {@paperscript} * @example {@paperscript}
* // Adding segments to a path using point objects: * // Adding segments to a path using point objects: