diff --git a/src/core/function/typed.js b/src/core/function/typed.js index c2b69624a2..966a1a4f78 100644 --- a/src/core/function/typed.js +++ b/src/core/function/typed.js @@ -124,6 +124,7 @@ export const createTyped = /* #__PURE__ */ factory('typed', dependencies, functi // string starting with an alphabetic character. It is used (at least) // in the definition of the derivative() function, as the argument telling // what to differentiate over must (currently) be a variable. + // TODO: deprecate the identifier type (it's not used anymore, see https://github.com/josdejong/mathjs/issues/3253) { name: 'identifier', test: s => isString && /^\p{L}[\p{L}\d]*$/u.test(s) diff --git a/src/function/algebra/derivative.js b/src/function/algebra/derivative.js index 93e19330bb..aad2f323b3 100644 --- a/src/function/algebra/derivative.js +++ b/src/function/algebra/derivative.js @@ -77,12 +77,20 @@ export const createDerivative = /* #__PURE__ */ factory(name, dependencies, ({ return options.simplify ? simplify(res) : res } - typed.addConversion( - { from: 'identifier', to: 'SymbolNode', convert: parse }) + function parseIdentifier (string) { + const symbol = parse(string) + if (!symbol.isSymbolNode) { + throw new TypeError('Invalid variable. ' + + `Cannot parse ${JSON.stringify(string)} into a variable in function derivative`) + } + return symbol + } const derivative = typed(name, { 'Node, SymbolNode': plainDerivative, - 'Node, SymbolNode, Object': plainDerivative + 'Node, SymbolNode, Object': plainDerivative, + 'Node, string': (node, symbol) => plainDerivative(node, parseIdentifier(symbol)), + 'Node, string, Object': (node, symbol, options) => plainDerivative(node, parseIdentifier(symbol), options) /* TODO: implement and test syntax with order of derivatives -> implement as an option {order: number} 'Node, SymbolNode, ConstantNode': function (expr, variable, {order}) { @@ -97,9 +105,6 @@ export const createDerivative = /* #__PURE__ */ factory(name, dependencies, ({ */ }) - typed.removeConversion( - { from: 'identifier', to: 'SymbolNode', convert: parse }) - derivative._simplify = true derivative.toTex = function (deriv) { diff --git a/test/unit-tests/function/algebra/derivative.test.js b/test/unit-tests/function/algebra/derivative.test.js index 9bf2d9075b..0f798abef7 100644 --- a/test/unit-tests/function/algebra/derivative.test.js +++ b/test/unit-tests/function/algebra/derivative.test.js @@ -202,6 +202,12 @@ describe('derivative', function () { compareString(derivative(math.parse('x^2'), math.parse('x')), '2 * x') }) + it('should accept string input containing special characters', function () { + // NOTE: we use `parse` here on purpose to see whether derivative accepts it + compareString(derivative('1 + x', 'x'), '1') + compareString(derivative('1 + $x', '$x'), '1') + }) + describe('expression parser', function () { it('should evaluate a derivative containing string value', function () { const res = math.evaluate('derivative("x^2", "x")') @@ -260,7 +266,7 @@ describe('derivative', function () { it('should throw error for incorrect argument types', function () { assert.throws(function () { derivative('42', '42') - }, /TypeError: Unexpected type of argument in function derivative \(expected: SymbolNode or identifier, actual: string, index: 1\)/) + }, /TypeError: Invalid variable. Cannot parse "42" into a variable in function derivative/) assert.throws(function () { derivative('[1, 2; 3, 4]', 'x') @@ -274,7 +280,7 @@ describe('derivative', function () { it('should throw error if incorrect number of arguments', function () { assert.throws(function () { derivative('x + 2') - }, /TypeError: Too few arguments in function derivative \(expected: SymbolNode or identifier, index: 1\)/) + }, /TypeError: Too few arguments in function derivative \(expected: string or SymbolNode or boolean, index: 1\)/) assert.throws(function () { derivative('x + 2', 'x', {}, true, 42)