@lookwe/omit-undefined 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # @lookwe/omit-undefined
2
+
3
+ A small utility package for TypeScript and JavaScript projects to create a new object with all `undefined` properties removed. It also provides a TypeScript helper type to accurately represent objects without `undefined` properties.
4
+
5
+ ## Installation
6
+
7
+ You can install `@lookwe/omit-undefined` using npm or yarn:
8
+
9
+ ```bash
10
+ npm install @lookwe/omit-undefined
11
+ # or
12
+ yarn add @lookwe/omit-undefined
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```js
18
+ import { omitUndefined, OmitUndefined } from '@lookwe/omit-undefined';
19
+
20
+ const cleanedObject = omitUndefined({
21
+ id: undefined,
22
+ name: 'Example',
23
+ isActive: true,
24
+ });
25
+
26
+ console.log(cleanedObject);
27
+ // {
28
+ // name: 'Example',
29
+ // description: true,
30
+ // }
31
+ ```
32
+
33
+ ### `OmitUndefined<T>` Type
34
+
35
+ This utility type helps you define the shape of an object where properties that could potentially be `undefined` in the original type `T` are now optional and guaranteed to not be `undefined`.
36
+
37
+ ```ts
38
+ import { OmitUndefined } from '@lookwe/omit-undefined';
39
+
40
+ type User = {
41
+ id: number | undefined;
42
+ username: string;
43
+ email?: string | undefined;
44
+ roles: string[] | undefined;
45
+ };
46
+
47
+ type DefinedUser = OmitUndefined<User>;
48
+ // DefinedUser will be:
49
+ // {
50
+ // id?: number;
51
+ // username: string;
52
+ // email?: string;
53
+ // roles?: string[];
54
+ // }
55
+
56
+ const validUser: DefinedUser = {
57
+ username: 'testuser',
58
+ };
59
+
60
+ const anotherValidUser: DefinedUser = {
61
+ id: 123,
62
+ username: 'anotheruser',
63
+ email: '[email address removed]',
64
+ roles: ['admin', 'editor'],
65
+ };
66
+
67
+ // The following would have a type error because 'username' is required in DefinedUser
68
+ // const invalidUser: DefinedUser = {};
69
+ ```
70
+
71
+ ## Usefulness with `exactOptionalPropertyTypes`
72
+
73
+ With the introduction of the `exactOptionalPropertyTypes` flag in TypeScript 4.4, a stricter distinction is made between an optional property being absent and being explicitly set to `undefined`. However, many existing utility types and even some libraries don't fully account for this nuanced behavior. They often treat optional properties (`propertyName?: Type`) as being equivalent to properties that can explicitly be `undefined` (`propertyName: Type | undefined`).
74
+
75
+ This can lead to situations where, with `exactOptionalPropertyTypes` enabled, you might expect an optional property with an `undefined` value to be treated as truly absent by utility functions or type definitions, but instead, it's still considered present (albeit with an `undefined` value).
76
+
77
+ `@lookwe/omit-undefined` and its `OmitUndefined` type are designed to be mindful of this distinction. The `omitUndefined` function explicitly removes properties whose value is `undefined`, ensuring that the resulting object truly reflects the absence of those properties. Similarly, the `OmitUndefined` type accurately represents an object where properties that _could_ have been `undefined` in the original type are now optional in the resulting type, but their possible values no longer include `undefined`.
78
+
79
+ This makes `@lookwe/omit-undefined` a valuable tool, especially when working with TypeScript projects that leverage the benefits of `exactOptionalPropertyTypes` for more precise type checking and a clearer understanding of optional properties. It helps bridge the gap where other libraries might fall short in fully supporting this stricter interpretation of optionality.
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Defines a type `OmitUndefinedProps` that creates a new type by making properties of `T` optional if their type
3
+ * extends `undefined`, and keeping other properties required.
4
+ */
5
+ export type OmitUndefined<T> = {
6
+ [K in keyof T as undefined extends T[K] ? K : never]?: Exclude<T[K], undefined>;
7
+ } & {
8
+ [K in keyof T as undefined extends T[K] ? never : K]: T[K];
9
+ };
10
+ /**
11
+ * Creates a new object containing only the properties of the input object that are not `undefined`.
12
+ *
13
+ * @param {T} obj - The object to process.
14
+ * @returns {OmitUndefined<T>} A new object with properties that were not `undefined` in the original object.
15
+ * @template T - An object type.
16
+ */
17
+ export declare function omitUndefined<const T extends object>(obj: T): OmitUndefined<T>;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Creates a new object containing only the properties of the input object that are not `undefined`.
3
+ *
4
+ * @param {T} obj - The object to process.
5
+ * @returns {OmitUndefined<T>} A new object with properties that were not `undefined` in the original object.
6
+ * @template T - An object type.
7
+ */
8
+ function omitUndefined(obj) {
9
+ const cleanedObj = { ...obj };
10
+ for (const key in cleanedObj) {
11
+ if (cleanedObj[key] === undefined) {
12
+ delete cleanedObj[key];
13
+ }
14
+ }
15
+ return cleanedObj;
16
+ }
17
+
18
+ export { omitUndefined };
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@lookwe/omit-undefined",
3
+ "description": "A small utility package for TypeScript and JavaScript projects to create a new object with all `undefined` properties removed, with strong support for TypeScript's `exactOptionalPropertyTypes`.",
4
+ "homepage": "https://github.com/Lookwe69/omit-undefined",
5
+ "bugs": {
6
+ "url": "https://github.com/Lookwe69/omit-undefined/issues"
7
+ },
8
+ "keywords": [
9
+ "omit",
10
+ "undefined",
11
+ "utility",
12
+ "typescript",
13
+ "javascript",
14
+ "exactOptionalPropertyTypes"
15
+ ],
16
+ "author": "Lookwe",
17
+ "version": "1.1.0",
18
+ "type": "module",
19
+ "scripts": {
20
+ "build": "rollup --config rollup-dist.config.js",
21
+ "build:watch": "npm run build -- --watch",
22
+ "build:prepack": "rollup --config rollup-prepack.config.js",
23
+ "clean": "rm -rf dist && rm -rf coverage && rm -rf internal",
24
+ "lint": "npm run lint:eslint && npm run lint:ts",
25
+ "lint:eslint": "eslint 'src/**/*.ts'",
26
+ "lint:ts": "tsc --noEmit",
27
+ "format": "prettier --check --ignore-path .gitignore .",
28
+ "format:fix": "npm run format -- --write && npm run format -- --write --plugin=@homer0/prettier-plugin-jsdoc",
29
+ "test": "npm run build && node --test",
30
+ "prepare-pack": "npm run clean && npm run build:prepack",
31
+ "prepack": "npm run prepare-pack",
32
+ "semantic-release": "semantic-release"
33
+ },
34
+ "files": [
35
+ "omit-undefined.js",
36
+ "omit-undefined.d.ts"
37
+ ],
38
+ "main": "omit-undefined.js",
39
+ "module": "omit-undefined.js",
40
+ "types": "omit-undefined.d.ts",
41
+ "exports": {
42
+ ".": "./omit-undefined.js"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "devDependencies": {
48
+ "@homer0/prettier-plugin-jsdoc": "^10.0.0",
49
+ "@ianvs/prettier-plugin-sort-imports": "^4.4.1",
50
+ "@rollup/plugin-node-resolve": "^16.0.1",
51
+ "@rollup/plugin-replace": "^6.0.2",
52
+ "@rollup/plugin-typescript": "^12.1.2",
53
+ "@tsconfig/strictest": "^2.0.5",
54
+ "@types/node": "^22.15.2",
55
+ "eslint": "^9.25.1",
56
+ "expect-type": "^1.2.1",
57
+ "globals": "^16.0.0",
58
+ "prettier": "^3.5.3",
59
+ "rollup": "^4.40.0",
60
+ "rollup-plugin-multi-input": "^1.5.0",
61
+ "semantic-release": "^24.2.3",
62
+ "tslib": "^2.8.1",
63
+ "typescript": "^5.8.3",
64
+ "typescript-eslint": "^8.31.0"
65
+ }
66
+ }