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...
.pipe(shell('node gulp/typescript/typescript-definition-generator.js'))
// ...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.
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.
const data = JSON.parse(fs.readFileSync(__dirname + '/typescript-definition-data.json', 'utf8'));
const classes = data.classes;
let globals = data.global.properties;
// Format classes.
classes.forEach(cls => {
@ -32,7 +31,7 @@ classes.forEach(cls => {
.filter(filter)
.map(it => ({
name: it._name,
type: formatType(it.type),
type: formatType(it.type, { isProperty: true, isSettableProperty: !it.readOnly }),
static: formatStatic(it.isStatic),
readOnly: formatReadOnly(it.readOnly),
comment: formatComment(it.comment)
@ -48,7 +47,7 @@ classes.forEach(cls => {
name: name,
// Constructors don't need return type.
type: !it.isConstructor
? formatType(getMethodReturnType(it), true)
? formatType(getMethodReturnType(it), { isMethodReturnType: true })
: '',
static: formatStatic(it.isStatic),
// This flag is only used below to filter methods.
@ -89,21 +88,25 @@ classes.forEach(cls => {
cls.hasStaticConstructors = cls.staticConstructors.length > 0;
});
// Format global vriables.
globals = globals
// Filter global variables that make no sense in type definition.
.filter(it => !/^on/.test(it._name) && it._name !== 'paper')
.map(it => ({
name: it._name,
type: formatType(it.type),
comment: formatComment(it.comment)
}));
// PaperScope class needs to be handled slightly differently because it "owns"
// all the other classes as properties. Eg. we can do `new paperScope.Path()`.
// So we add a `classesPointers` property that the template will use.
const paperScopeClass = classes.find(_ => _.className === 'PaperScope');
paperScopeClass.classesPointers = classes.filter(_ => _.className !== 'PaperScope').map(_ => ({ name: _.className }));
// Since paper.js module is at the same time a PaperScope instance, we need to
// 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.
// Prepare data for the template.
const context = {
paperInstance: paperInstance,
classes: classes,
globals: globals,
version: data.version,
date: data.date,
// {{#doc}} blocks are used in template to automatically generate a JSDoc
@ -130,48 +133,69 @@ function formatStatic(isStatic) {
return isStatic ? 'static ' : null;
}
function formatType(type, isMethodReturnType, staticConstructorClass) {
return ': ' + parseType(type, isMethodReturnType, staticConstructorClass);
function formatType(type, options) {
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
// `void` for method return type and `any` for the rest.
if (!type) {
return isMethodReturnType ? 'void' : 'any';
}
if (type === '*') {
return 'any';
return options.isMethodReturnType ? 'void' : 'any';
}
// Prefer `any[]` over `Array<any>` to be more consistent with other types.
if (type === 'Array') {
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
// back parsed types.
return type.split('|').map(type => {
// Handle rest parameter pattern: `...Type` => `Type[]`
const matches = type.match(/^\.\.\.(.+)$/);
if (matches) {
return parseType(matches[1]) + '[]';
}
type = type.split('|').map(splittedType => {
// 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
// example, in `Path.Rectangle(rectangle: Rectangle)` method,
// `rectangle` parameter type must be mapped to `paper.Rectangle` as it
// is declared inside a `Path` namespace and would otherwise be wrongly
// 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
// typescript best practices.
return ['Number', 'String', 'Boolean', 'Object'].indexOf(singleType) >= 0
? type.toLowerCase()
: type;
if (['Number', 'String', 'Boolean', 'Object'].indexOf(singleType) >= 0) {
splittedType = splittedType.toLowerCase();
}
// 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(' | ');
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) {
@ -195,7 +219,7 @@ function formatParameter(param, staticConstructorClass) {
if (param.isOptional) {
content += '?';
}
content += formatType(param.type, false, staticConstructorClass);
content += formatType(param.type, { staticConstructorClass });
return content;
}
@ -306,11 +330,9 @@ function sortMethods(methodA, methodB) {
if (methodB.params === 'object: object') {
return -1;
}
}
else if (aIsContructor) {
} else if (aIsContructor) {
return -1;
}
else if (bIsContructor) {
} else if (bIsContructor) {
return 1;
}
return 0;

View file

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

View file

@ -13,17 +13,6 @@
import * as paper from 'paper';
//
// Global
//
paper.project;
paper.projects;
paper.view;
paper.tool;
paper.tools;
//
// Utility variables
//
@ -368,6 +357,7 @@ item.strokeScaling;
item.dashArray;
item.miterLimit;
item.fillColor;
item.fillColor && item.fillColor.red;
item.fillRule;
item.shadowColor;
item.shadowBlur;
@ -538,6 +528,8 @@ raster.source;
raster.crossOrigin;
raster.smoothing;
raster.onLoad;
raster.onLoad = () => {};
raster.onLoad = null;
raster.onError;
raster.getSubCanvas(rectangle);
raster.getSubRaster(rectangle);
@ -662,6 +654,9 @@ path.length;
path.area;
path.fullySelected;
path.add(segment);
path.add(point);
path.add([0,0]);
path.add(segment, point, [0,0]);
path.insert(0, segment);
path.addSegments([ segment ]);
path.insertSegments(0, [ segment ]);
@ -1048,6 +1043,7 @@ view.responds('');
event.timeStamp;
event.modifiers;
event.modifiers.shift;
event.preventDefault();
event.stopPropagation();
event.stop();
@ -1128,6 +1124,7 @@ keyEvent.toString();
new paper.PaperScope();
paperScope.version;
paperScope.settings;
paperScope.settings = null;
paperScope.project;
paperScope.projects;
paperScope.view;
@ -1141,6 +1138,60 @@ paperScope.setup({} as HTMLCanvasElement);
paperScope.setup(size);
paperScope.activate();
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.
*
* @type String
* @readonly
*/
version: /*#=*/__options.version,

View file

@ -63,6 +63,7 @@
*
* @name 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
* this path.
*
* @param {Segment|Point} segment the segment or point to be added.
* @return {Segment} the added segment. This is not necessarily the same
* object, e.g. if the segment to be added already belongs to another path
* @param {...(Segment|Point|Number[])} segment the segment or point to be
* added.
* @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}
* // Adding segments to a path using point objects: