Skip to content

[code-infra] Convert @mui/utils to typescript #45671

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Apr 9, 2025
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages-internal/test-utils/src/chai.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ declare global {
* Asserts that the given callback throws an error matching the given message in development (process.env.NODE_ENV !== 'production').
* In production it expects a minified error.
*/
toThrowMinified(message: string): void;
toThrowMinified(message: string | RegExp): void;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ describe('capitalize', () => {

it('should throw when not used correctly', () => {
expect(() => {
// @ts-expect-error Testing improper usage
capitalize();
}).toThrowMinified(/expects a string argument/);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { spy, useFakeTimers } from 'sinon';
import debounce from './debounce';

describe('debounce', () => {
let clock;
let clock: ReturnType<typeof useFakeTimers>;

beforeEach(() => {
clock = useFakeTimers();
Expand All @@ -16,8 +16,8 @@ describe('debounce', () => {
it('should debounce', () => {
const handler = spy();
const expectedContext = { foo: 'bar' };
let actualContext;
function collectContext(...args) {
let actualContext: any;
function collectContext(this: any, ...args: any[]) {
// eslint-disable-next-line consistent-this
actualContext = this;
handler(...args);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('deprecatedPropType', () => {

it('should not warn', () => {
const propName = `children${new Date()}`;
const props = {};
const props: Record<string, any> = {};
PropTypes.checkPropTypes(
{
[propName]: deprecatedPropType(PropTypes.string, 'give me a reason'),
Expand All @@ -26,7 +26,7 @@ describe('deprecatedPropType', () => {

it('should warn once', () => {
const propName = `children`;
const props = {
const props: Record<string, any> = {
[propName]: 'yolo',
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
export default function deprecatedPropType(validator, reason) {
import { Validator } from 'prop-types';

export default function deprecatedPropType<T>(
validator: Validator<T>,
reason: string,
): Validator<T> {
if (process.env.NODE_ENV === 'production') {
return () => null;
}

return (props, propName, componentName, location, propFullName) => {
return (
props: Record<string, any>,
propName: string,
componentName?: string,
location?: string,
propFullName?: string,
) => {
const componentNameSafe = componentName || '<<anonymous>>';
const propFullNameSafe = propFullName || propName;

Expand Down
5 changes: 0 additions & 5 deletions packages/mui-utils/src/integerPropType/integerPropType.d.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ describe('integerPropType', () => {
const location = '';
const componentName = 'DummyComponent';

function checkPropType(props, propName, required) {
function checkPropType(props: Record<string, any>, propName: string, required: boolean): void {
PropTypes.checkPropTypes(
{
[propName]: required ? integerPropType.isRequired : integerPropType,
Expand All @@ -20,13 +20,21 @@ describe('integerPropType', () => {
);
}

function assertPass({ props }, propName, required = false) {
function assertPass(
{ props }: { props: Record<string, any> },
propName: string,
required = false,
): void {
expect(() => {
checkPropType(props, propName, required);
}).not.toErrorDev();
}

function assertFail({ props }, propName, required = false) {
function assertFail(
{ props }: { props: Record<string, any> },
propName: string,
required = false,
): void {
const propType = getTypeByValue(props[propName]);
const errorMessage = `Warning: Failed type: Invalid ${location} \`${propName}\` of type \`${propType}\` supplied to \`${componentName}\`, expected \`integer\`.`;

Expand All @@ -41,36 +49,44 @@ describe('integerPropType', () => {
});

it('passes on undefined', () => {
// @ts-expect-error div doesn't have an a prop
assertPass(<div a={undefined} />, 'a');
});

it('fails on null', () => {
// @ts-expect-error div doesn't have an a prop
assertFail(<div a={null} />, 'a');
});
});

// @ts-expect-error div doesn't have an a prop
it('passes on zero', () => assertPass(<div a={0} />, 'a'));

it('passes on positive numbers', () => {
// @ts-expect-error div doesn't have an a prop
assertPass(<div a={42} />, 'a');
});

describe('passes with the conversion before passing', () => {
it('passes with conversion - parseInt', () => {
assertPass(<div a={parseInt(1.1, 10)} />, 'a');
// @ts-expect-error div doesn't have an a prop
assertPass(<div a={parseInt('1.1', 10)} />, 'a');
});

it('passes with the conversion - Math.floor', () => {
// @ts-expect-error div doesn't have an a prop
assertPass(<div a={Math.floor(1.1)} />, 'a');
});

it('passes with the boolean conversion', () => {
// @ts-expect-error div doesn't have an a prop
// eslint-disable-next-line no-bitwise
assertPass(<div a={1.1 | 0} />, 'a');
});
});

it('passes on negative numbers', () => {
// @ts-expect-error div doesn't have an a prop
assertPass(<div a={-42} />, 'a');
});

Expand All @@ -80,32 +96,39 @@ describe('integerPropType', () => {
});

it('fails when we pass float number', () => {
// @ts-expect-error div doesn't have an a prop
assertFail(<div a={1.5} />, 'a');
});

it('fails when have been made computation which results in float number', () => {
// @ts-expect-error div doesn't have an a prop
assertFail(<div a={(0.1 + 0.2) * 10} />, 'a');
});

it('fails on string', () => {
// @ts-expect-error div doesn't have an a prop
assertFail(<div a={'a message'} />, 'a');
});

it('fails on boolean', () => {
// @ts-expect-error div doesn't have an a prop
assertFail(<div a={false} />, 'a');
});

it('fails on array', () => {
// @ts-expect-error div doesn't have an a prop
assertFail(<div a={[]} />, 'a');
});
});

describe('fails on number edge cases', () => {
it('fails on infinity', () => {
// @ts-expect-error div doesn't have an a prop
assertFail(<div a={Infinity} />, 'a');
});

it('fails on NaN', () => {
// @ts-expect-error div doesn't have an a prop
assertFail(<div a={NaN} />, 'a');
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export function getTypeByValue(value) {
import PropTypes from 'prop-types';

export function getTypeByValue(value: any): string {
const valueType = typeof value;
switch (valueType) {
case 'number':
Expand All @@ -24,7 +26,12 @@ export function getTypeByValue(value) {
}
}

function requiredInteger(props, propName, componentName, location) {
function requiredInteger(
props: Record<string, any>,
propName: string,
componentName: string,
location: string,
): Error | null {
const propValue = props[propName];

if (propValue == null || !Number.isInteger(propValue)) {
Expand All @@ -38,21 +45,29 @@ function requiredInteger(props, propName, componentName, location) {
return null;
}

function validator(props, propName, ...other) {
function validator(
props: Record<string, any>,
propName: string,
componentName: string,
location: string,
): Error | null {
const propValue = props[propName];

if (propValue === undefined) {
return null;
}

return requiredInteger(props, propName, ...other);
return requiredInteger(props, propName, componentName, location);
}

function validatorNoop() {
function validatorNoop(): null {
return null;
}

validator.isRequired = requiredInteger;
validatorNoop.isRequired = validatorNoop;

export default process.env.NODE_ENV === 'production' ? validatorNoop : validator;
const integerPropType: PropTypes.Requireable<number> =
process.env.NODE_ENV === 'production' ? validatorNoop : validator;

export default integerPropType;

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import requirePropFactory from './requirePropFactory';

describe('requirePropFactory', () => {
const componentNameInError = 'componentNameInError';
let requireProp;
let requireProp: (prop: string) => PropTypes.Validator<any>;

before(() => {
requireProp = requirePropFactory(componentNameInError);
Expand All @@ -18,7 +18,7 @@ describe('requirePropFactory', () => {
describe('requireProp()', () => {
const requiredPropName = 'requiredPropName';

let requirePropValidator;
let requirePropValidator: PropTypes.Validator<any>;

before(() => {
requirePropValidator = requireProp(requiredPropName);
Expand All @@ -29,8 +29,8 @@ describe('requirePropFactory', () => {
});

describe('requirePropValidator', () => {
let props;
let propName;
let props: Record<string, unknown>;
let propName: string;

beforeEach(() => {
PropTypes.resetWarningCache();
Expand Down Expand Up @@ -71,13 +71,14 @@ describe('requirePropFactory', () => {
});

describe('propName is in props and requiredProp not in props', () => {
let result;
let result: Error | null;

before(() => {
props = {};
propName = 'propName';
props[propName] = true;
delete props[requiredPropName];
// @ts-expect-error The validator should be called with the right arguments
result = requirePropValidator(props, propName, undefined, undefined, undefined);
});

Expand All @@ -97,19 +98,20 @@ describe('requirePropFactory', () => {
});

describe('propFullName given to validator', () => {
let propFullName;
let propFullName: string;

before(() => {
propFullName = 'propFullName';
// @ts-expect-error The validator should be called with the right arguments
result = requirePropValidator(props, propName, undefined, undefined, propFullName);
});

it('returned error message should have propFullName', () => {
expect(result.message.includes(propFullName)).to.equal(true);
expect(result!.message.includes(propFullName)).to.equal(true);
});

it('returned error message should not have propName', () => {
expect(result.message.includes(propName)).to.equal(false);
expect(result!.message.includes(propName)).to.equal(false);
});
});
});
Expand All @@ -122,7 +124,7 @@ describe('requirePropFactory', () => {
test: PropTypes.string,
};

const localProps = {};
const localProps: Record<string, unknown> = {};
const localPropName = 'test';
localProps[localPropName] = 'string';

Expand Down Expand Up @@ -150,7 +152,7 @@ describe('requirePropFactory', () => {
test: PropTypes.string,
};

const localProps = {};
const localProps: Record<string, unknown> = {};
const localPropName = 'test';
localProps[localPropName] = true;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
export default function requirePropFactory(componentNameInError, Component) {
import * as React from 'react';
import PropTypes from 'prop-types';

export default function requirePropFactory(
componentNameInError: string,
Component?: React.ComponentType<unknown>,
): (requiredProp: string) => PropTypes.Validator<any> {
if (process.env.NODE_ENV === 'production') {
return () => null;
return () => () => null;
}

// eslint-disable-next-line react/forbid-foreign-prop-types
const prevPropTypes = Component ? { ...Component.propTypes } : null;

const requireProp =
(requiredProp) =>
(requiredProp: string): PropTypes.Validator<any> =>
(props, propName, componentName, location, propFullName, ...args) => {
const propFullNameSafe = propFullName || propName;

Expand Down
7 changes: 0 additions & 7 deletions packages/mui-utils/src/unsupportedProp/unsupportedProp.d.ts

This file was deleted.

Loading
Loading