Skip to content

Disallow comparison against NaNΒ #49962

Closed
@arzeth

Description

@arzeth

Suggestion

πŸ” Search Terms

disallow NaN
comparing with NaN
disallow comparing NaN

βœ… Viability Checklist

My suggestion meets these guidelines:

  • [?] This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • βœ… This wouldn't change the runtime behavior of existing JavaScript code
  • βœ… This could be implemented without emitting different JS based on the types of the expressions
  • βœ… This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • βœ… This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

Similar to #45978 . So the error should be something like The condition will always return 'false' since NaN !== NaN. Use Number.isNaN.

πŸ“ƒ Motivating Example

function setReputation (n: number)
{
  // ! here is the bug:
  if (n === NaN) throw new Error('setReputation:: Impossible reputation')
  // update the record in a remote DB
}
const foundGolems = 0
const killedGolems = 0
setReputation(killedGolems / foundGolems * 100)
// 0/0 is NaN

or what has actually happened to me:

export function clamp ( // @license CC0
	n:   number,
	min: number|bigint,
	max: number|bigint,
): typeof n
export function clamp (
	n:   bigint,
	min: number|bigint,
	max: number|bigint,
): typeof n
export function clamp (
	n:   number|bigint,
	min: number|bigint,
	max: number|bigint,
): typeof n
{
	// +null === 0
	// +'' === 0
	// +undefined === NaN
	const nFixed = typeof n === 'bigint' ? n : (
		// @ts-ignore
		'' === n || n === null || typeof n === 'symbol'
		? NaN : +n
	)
	const minFixed = typeof min === 'bigint' ? min : (
		// @ts-ignore
		'' === min || min === null || typeof min === 'symbol'
		? NaN : +min
	)
	const maxFixed = typeof max === 'bigint' ? max : (
		// @ts-ignore
		'' === max || max === null || typeof max === 'symbol'
		? NaN : +max
	)
	if (nFixed === NaN)
	{
		const ret = nFixed
		//const ret = min == max ? (typeof min === 'bigint' ? +min.toString() : min) : nFixed
		console.warn(
			'clamp(n=%O, min=%O, max=%O):: n === NaN; returning %O',
			n, min, max, ret,
		)
		return ret
	}
	let ret: typeof n
	if (
		minFixed === NaN
		&&
		maxFixed === NaN
	)
	{
		ret = nFixed
		console.warn(
			'clamp(n=%O, min=%O, max=%O):: min === NaN && max === NaN; returning `n` (%O)',
			n, min, max, ret,
		)
	}
	else if (maxFixed < minFixed)
	{
		// throw new RangeError
		//ret = maxFixed // similar to torch.clamp
		ret = minFixed // because arguments are specified in the left-to-right *order*.
		//ret = nFixed < maxFixed ? maxFixed : (nFixed > minFixed ? minFixed : nFixed)
		/*console.warn(
			'clamp(n=%O, min=%O, max=%O):: max<min; returning `min` (%O)',
			n, min, max, ret
		)*/
	}
	else
	{
		ret = nFixed < minFixed ? minFixed : (nFixed > maxFixed ? maxFixed : nFixed)
	}
	if (typeof n === 'bigint')
	{
		return typeof ret === 'bigint' ? ret : BigInt(Math.floor(ret))
	}
	if (typeof ret === 'bigint')
	{
		return +ret.toString()
	}
	return ret
}

πŸ’» Use Cases

Since NaN is very rare, a programmer could forget to use Number.isNaN(variable), and accidentally use ===.
Or a programmer could have never even learned this, and just applies knowledge from other langs where NaN === NaN.

Metadata

Metadata

Assignees

No one assigned

    Labels

    CommittedThe team has roadmapped this issueExperience EnhancementNoncontroversial enhancementsFixedA PR has been merged for this issueSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions