Skip to content

Commit e91e949

Browse files
committed
Fixed issues with usage of generics in Typescript declarations (#529)
1 parent ca39960 commit e91e949

File tree

5 files changed

+326
-266
lines changed

5 files changed

+326
-266
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
1+
v2.0.5
2+
======
3+
4+
- More fixes for Typescript declarations including better handling of generics.
5+
6+
v2.0.4
7+
======
8+
9+
- Fixed issue with Typescript declarations not exporting all packages.
10+
111
v2.0.3
212
======
313

414
- Updated Typescript declaration files to better handle modules.
15+
- Added type guards to isString... declarations.
516
- Fixed issue with Range date unit methods on invalid ranges.
617

718
v2.0.2

gulpfile.js

Lines changed: 82 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3527,16 +3527,18 @@ function buildTypescriptDeclarations() {
35273527
function exportTypescriptDeclarations(basePath, allowedModules, include, exclude) {
35283528

35293529
var optionInterfaceNamespaces = {};
3530+
var moduleCallbackGenerics = {};
3531+
var moduleOptionsGenerics = {};
35303532

35313533
var whitelist = getMethodList(include);
35323534
var blacklist = getMethodList(exclude);
35333535

3534-
var TYPE_GUARD_REG = /is(Boolean|Number|String|Date|RegExp|Function|Array|Error|Set|Map)$/;
3536+
var TYPE_REG = /is(Boolean|Number|String|Date|RegExp|Function|Array|Error|Set|Map)$/;
35353537

35363538
var TYPE_GUARD_GENERICS = {
3537-
Array: ['T'],
3538-
Map: ['K', 'V'],
3539-
Set: ['T']
3539+
Array: ['any'],
3540+
Map: ['any', 'any'],
3541+
Set: ['any']
35403542
}
35413543

35423544
var CHAINABLE_RAW_TYPE = 'RawValue';
@@ -3749,7 +3751,7 @@ function exportTypescriptDeclarations(basePath, allowedModules, include, exclude
37493751
}
37503752

37513753
function mapType(type) {
3752-
return 'type ' + type.name + ' = ' + type.type + ';'
3754+
return 'type ' + type.name + (type.generics || '') + ' = ' + type.type + ';'
37533755
}
37543756

37553757
addBlock(module.types, '\n'.repeat(top ? 2 : 1), mapType);
@@ -3824,13 +3826,12 @@ function exportTypescriptDeclarations(basePath, allowedModules, include, exclude
38243826

38253827
// Set up all remaining methods.
38263828

3827-
var instanceParam = {
3828-
name: 'instance',
3829-
required: true,
3830-
type: rawType
3831-
};
3832-
38333829
namespace.methods.forEach(function(method) {
3830+
var instanceParam = {
3831+
name: 'instance',
3832+
required: true,
3833+
type: getInstanceParamType(method, rawType)
3834+
};
38343835
var method = clone(method);
38353836
if (method.type === 'instance') {
38363837
method.params.unshift(instanceParam);
@@ -3882,13 +3883,13 @@ function exportTypescriptDeclarations(basePath, allowedModules, include, exclude
38823883
// Add object instance methods as static, i.e.
38833884
// Object.forEach, etc.
38843885
if (namespace.name === 'Object') {
3885-
var objectInstanceParam = {
3886-
name: 'instance',
3887-
type: 'Object',
3888-
required: true
3889-
};
38903886
var objectInstanceMethods = getExtendedMethods(namespace, 'instance');
38913887
getExtendedMethods(namespace, 'instance').forEach(function(method) {
3888+
var objectInstanceParam = {
3889+
name: 'instance',
3890+
type: getInstanceParamType(method, 'Object'),
3891+
required: true
3892+
};
38923893
method = clone(method);
38933894
method.params.unshift(objectInstanceParam);
38943895
if (method.signatures) {
@@ -4053,7 +4054,7 @@ function exportTypescriptDeclarations(basePath, allowedModules, include, exclude
40534054

40544055
function getGenericSource(generic) {
40554056
if (generic instanceof Array) {
4056-
generic = generic.join(', ');
4057+
generic = getUniqueGenerics(generic).join(', ');
40574058
}
40584059
if (!generic) {
40594060
return '';
@@ -4063,7 +4064,18 @@ function exportTypescriptDeclarations(basePath, allowedModules, include, exclude
40634064
return generic;
40644065
}
40654066

4067+
function getUniqueGenerics(generics) {
4068+
var result = [];
4069+
generics.forEach(function(g) {
4070+
if (g === 'any' || result.indexOf(g) === -1) {
4071+
result.push(g);
4072+
}
4073+
});
4074+
return result;
4075+
}
4076+
40664077
function addMethodGenerics(method, generics) {
4078+
generics = generics || [];
40674079
if (typeof generics === 'string') {
40684080
generics = [generics];
40694081
}
@@ -4080,6 +4092,14 @@ function exportTypescriptDeclarations(basePath, allowedModules, include, exclude
40804092

40814093
/* ------------ Methods -------------- */
40824094

4095+
function isTypeCheck(method) {
4096+
return method.name.match(TYPE_REG);
4097+
}
4098+
4099+
function getInstanceParamType(method, type) {
4100+
return isTypeCheck(method) ? 'any' : type;
4101+
}
4102+
40834103
function buildMethods(methods, namespace, mode) {
40844104
methods.sort(collateMethods);
40854105
methods = methods.map(function(method) {
@@ -4113,22 +4133,32 @@ function exportTypescriptDeclarations(basePath, allowedModules, include, exclude
41134133
src += (param.type || '').split('|').map(function(type) {
41144134
type = getType(type, method) || 'undefined';
41154135
if (type.match(/Fn$/)) {
4136+
var callback = method.callbacks.find(function(callback) {
4137+
return callback.name === type;
4138+
});
41164139
if (param.type.indexOf('|') === -1) {
41174140
// If a callback is the ONLY type allowed for this parameter
41184141
// (no alternates), then it can be inlined directly into the
41194142
// definition, so do that here. Otherwise it needs to be moved
41204143
// out into a named type and referenced.
4121-
var callback = method.callbacks.find(function(callback) {
4122-
return callback.name === type;
4123-
});
41244144
type = getFunctionSource(callback);
4125-
} else if (mode === 'extended') {
4126-
// If we are in extended mode and referencing a callback interface,
4127-
// then it needs to have its fully qualified namespace.
4128-
type = ['sugarjs', namespace.name, type].join('.');
4145+
} else {
4146+
if (mode === 'extended') {
4147+
// If we are in extended mode and referencing a callback interface,
4148+
// then it needs to have its fully qualified namespace.
4149+
type = ['sugarjs', namespace.name, type].join('.');
4150+
}
4151+
var generics = moduleCallbackGenerics[namespace.name + ':' + callback.name];
4152+
if (generics) {
4153+
addMethodGenerics(method, generics);
4154+
type += getGenericSource(generics);
4155+
}
41294156
}
41304157
} else if (type.match(/Options$/)) {
41314158
var optionsNamespace = optionInterfaceNamespaces[type];
4159+
var generics = moduleOptionsGenerics[namespace.name + ':' + type];
4160+
type += getGenericSource(generics);
4161+
addMethodGenerics(method, generics);
41324162
if (mode === 'extended') {
41334163
// If we are in extended mode and referencing an options inteface,
41344164
// then it needs to have its fully qualified namespace.
@@ -4160,7 +4190,7 @@ function exportTypescriptDeclarations(basePath, allowedModules, include, exclude
41604190
}
41614191

41624192
function requiresTypeGuard() {
4163-
return method.name.match(TYPE_GUARD_REG) &&
4193+
return isTypeCheck(method) &&
41644194
(mode === 'static' || mode === 'extended');
41654195
}
41664196

@@ -4184,8 +4214,7 @@ function exportTypescriptDeclarations(basePath, allowedModules, include, exclude
41844214
var type = method.name.replace(/^is/, '');
41854215
var generics = TYPE_GUARD_GENERICS[type];
41864216
if (generics) {
4187-
addMethodGenerics(method, generics);
4188-
type += '<' + generics.join(',') + '>';
4217+
type += getGenericSource(generics);
41894218
}
41904219
src = getTypeGuard(type);
41914220
} else if ((match = src.match(/Array<(.+)>/))) {
@@ -4446,9 +4475,14 @@ function exportTypescriptDeclarations(basePath, allowedModules, include, exclude
44464475
return t.name === callback.name;
44474476
});
44484477
if (!typeExists) {
4478+
var signature = getCallbackSignature(callback, method, namespace);
4479+
if (method.generics) {
4480+
moduleCallbackGenerics[namespace.name + ':' + callback.name] = method.generics;
4481+
}
44494482
module.types.push({
44504483
name: callback.name,
4451-
type: getCallbackSignature(callback, method, namespace)
4484+
generics: getGenericSource(callback.generics),
4485+
type: signature
44524486
});
44534487
}
44544488
}
@@ -4482,12 +4516,21 @@ function exportTypescriptDeclarations(basePath, allowedModules, include, exclude
44824516
return param.type.match(/Options$/);
44834517
});
44844518

4519+
var optionsGenerics = [];
4520+
44854521
var src = method.options.map(function(option) {
44864522
var types = option.type.split('|');
44874523
types = types.map(function(type) {
4488-
if (types.length === 1 && type.match(/Fn$/)) {
4524+
if (type.match(/Fn$/)) {
44894525
var callback = findMethodCallbackByName(method, type);
4490-
type = getCallbackSignature(callback, method, namespace);
4526+
if (types.length === 1 && type.match(/Fn$/)) {
4527+
// Inline a callback delcaration
4528+
type = getCallbackSignature(callback, method, namespace);
4529+
} else {
4530+
// Refer to callback externally
4531+
type += getGenericSource(moduleCallbackGenerics[namespace.name + ':' + callback.name]);
4532+
}
4533+
optionsGenerics = optionsGenerics.concat(callback.generics);
44914534
}
44924535
return type;
44934536
});
@@ -4500,14 +4543,20 @@ function exportTypescriptDeclarations(basePath, allowedModules, include, exclude
45004543
optionInterfaceNamespaces[optionsParam.type] = namespace.name;
45014544
}
45024545

4503-
module.interfaces.push(getInterfaceSource(optionsParam.type, src));
4546+
moduleOptionsGenerics[namespace.name + ':' + optionsParam.type] = optionsGenerics;
4547+
var name = optionsParam.type + getGenericSource(optionsGenerics);
4548+
module.interfaces.push(getInterfaceSource(name, src));
45044549
});
45054550
}
45064551

45074552
function getCallbackSignature(callback, method, namespace) {
4553+
var genericsLength = method.generic && method.generics.length || 0, gen;
45084554
var params = getParams(callback.params, method, namespace);
45094555
var returns = callback.returns ? getType(callback.returns, method) : 'void';
4510-
return getGenericSource(method.generics) + '(' + params + ') => ' + returns;
4556+
// Slightly convoluted way of getting generics for the callback by
4557+
// subtracting method generics from what they were previously.
4558+
callback.generics = method.generics.slice(genericsLength);
4559+
return '(' + params + ') => ' + returns;
45114560
}
45124561

45134562
function findMethodCallbackByName(method, name) {
@@ -4541,10 +4590,10 @@ function exportTypescriptDeclarations(basePath, allowedModules, include, exclude
45414590
sugarjs.interfaces.push(getRangeInterface(namespace));
45424591
} else {
45434592
var module = getDeclaredNamespace(namespace.name);
4593+
addExtras(module);
45444594
module.interfaces.push(getConstructorInterface(namespace, module));
45454595
module.interfaces.push(getChainableBaseInterface(namespace));
45464596

4547-
addExtras(module);
45484597

45494598
// Export extended interfaces unless opting out.
45504599
if (args['extended-mode'] !== false) {

lib/array.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ defineStatic(sugarArray, {
426426
* @param {number} n
427427
* @param {indexMapFn} map
428428
* @callbackParam {number} i
429-
* @callbackReturns {any} indexMapFn
429+
* @callbackReturns {ArrayElement} indexMapFn
430430
*
431431
***/
432432
'construct': function(n, fn) {

0 commit comments

Comments
 (0)