🤖 Merge PR #65820 feat: Add types for kebabcase-keys by @yutak23

This commit is contained in:
yutak23
2023-06-20 06:32:04 +09:00
committed by GitHub
parent 58d005e8a4
commit 729e11e1cb
4 changed files with 199 additions and 0 deletions

123
types/kebabcase-keys/index.d.ts vendored Normal file
View File

@@ -0,0 +1,123 @@
// Type definitions for kebabcase-keys 1.0
// Project: https://github.com/mattii/kebabcase-keys#readme
// Definitions by: Yuta Katayama <https://github.com/yutak23>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// Minimum TypeScript Version: 4.1
/**
* Return a default type if input type is nil.
* @template T - Input type.
* @template U - Default type.
*/
type WithDefault<T, U extends T> = T extends undefined | null ? U : T;
type CamelToKebab<S extends string> = S extends `${infer T}${infer U}`
? `${T extends Capitalize<T> ? '-' : ''}${Lowercase<T>}${CamelToKebab<U>}`
: S;
type PascalToKebab<S extends string> = CamelToKebab<Uncapitalize<S>>;
type SnakeToKebab<S extends string> = S extends `${infer T}_${infer U}` ? `${T}-${SnakeToKebab<U>}` : S;
type SpaceToKebab<S extends string> = S extends `${infer T}${Whitespace}${infer U}` ? `${T}-${SnakeToKebab<U>}` : S;
type ContainWordSeparatorsToKebab<S extends string> = S extends `${infer T}-${infer U}`
? S
: S extends `${infer T}_${infer U}`
? SnakeToKebab<Lowercase<S>>
: SpaceToKebab<S>;
type AnyCaseToKebab<S extends string> = S extends `${infer T}${WordSeparators}${infer U}`
? ContainWordSeparatorsToKebab<S>
: S extends Uppercase<S>
? Lowercase<S>
: S extends Capitalize<S>
? PascalToKebab<S>
: CamelToKebab<S>;
type KebabCase<S extends string | number | symbol> = S extends number
? `${S}`
: S extends symbol
? never
: S extends string
? AnyCaseToKebab<S>
: S;
type KebabCasedProperties<T, Deep extends boolean = false> = T extends readonly CustomJsonObject[]
? {
[Key in keyof T]: KebabCasedProperties<T[Key], Deep>;
}
: T extends CustomJsonObject
? {
[Key in keyof T as KebabCase<Key>]: T[Key] extends CustomJsonObject | CustomJsonObject[]
? Deep[] extends Array<true>
? KebabCasedProperties<T[Key], Deep>
: T[Key]
: T[Key];
}
: T;
interface Options {
/**
* Recurse nested objects and objects in arrays.
* @default false
*/
readonly deep?: boolean;
/**
* Exclude keys from being kebab-cased.
* @default []
*/
readonly exclude?: ReadonlyArray<string | RegExp>;
}
/**
* Convert object keys to kebabcase.
* @param input - Object or array of objects to snake-case.
* @param options - Options of conversion.
*/
declare function kebabcaseKeys<T extends CustomJsonObject | CustomJsonObject[], OptionsType extends Options>(
input: T,
options?: OptionsType,
): KebabCasedProperties<T, WithDefault<OptionsType['deep'], false>>;
export = kebabcaseKeys;
// Extended versions of https://github.com/sindresorhus/type-fest#json
type CustomJsonObject = { [Key in string]: CustomJsonValue } & {
[Key in string]?: CustomJsonValue | undefined;
};
type CustomJsonValue = JsonPrimitive | object | CustomJsonObject | CustomJsonArray;
type CustomJsonArray = CustomJsonValue[] | readonly CustomJsonValue[];
// based on https://github.com/DefinitelyTyped/DefinitelyTyped/pull/59806#pullrequestreview-942584759
// Copied from https://github.com/sindresorhus/type-fest
type JsonPrimitive = string | number | boolean | null;
type WordSeparators = '-' | '_' | Whitespace;
type Whitespace =
| '\u{9}' // '\t'
| '\u{A}' // '\n'
| '\u{B}' // '\v'
| '\u{C}' // '\f'
| '\u{D}' // '\r'
| '\u{20}' // ' '
| '\u{85}'
| '\u{A0}'
| '\u{1680}'
| '\u{2000}'
| '\u{2001}'
| '\u{2002}'
| '\u{2003}'
| '\u{2004}'
| '\u{2005}'
| '\u{2006}'
| '\u{2007}'
| '\u{2008}'
| '\u{2009}'
| '\u{200A}'
| '\u{2028}'
| '\u{2029}'
| '\u{202F}'
| '\u{205F}'
| '\u{3000}'
| '\u{FEFF}';

View File

@@ -0,0 +1,52 @@
import kebabcaseKeys = require('kebabcase-keys');
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
interface Person {
name: string;
age: number;
}
const point = new Point(0, 10);
const person: Person = {
name: 'John',
age: 30,
};
const symbol = Symbol('foo');
kebabcaseKeys({}); // $ExpectType {}
kebabcaseKeys({ foo_bar: true }); // $ExpectType { "foo-bar": true; }
kebabcaseKeys({ foo_bar: true, nested: { unicorn_rainbow: true } }); // $ExpectType { "foo-bar": true; nested: { unicorn_rainbow: true; }; }
kebabcaseKeys({ foo_bar: true, nested: { unicorn_rainbow: true } }, { deep: true }); // $ExpectType { "foo-bar": true; nested: { "unicorn-rainbow": true; }; }
kebabcaseKeys([]); // $ExpectType never[]
kebabcaseKeys([{}]); // $ExpectType {}[]
kebabcaseKeys([{ foo_bar: true }]); // $ExpectType { "foo-bar": true; }[]
kebabcaseKeys([{ foo_bar: true, nested: { unicorn_rainbow: true } }]); // $ExpectType { "foo-bar": true; nested: { unicorn_rainbow: true; }; }[]
kebabcaseKeys([{ foo_bar: true, nested: { unicorn_rainbow: true } }], { deep: true }); // $ExpectType { "foo-bar": true; nested: { "unicorn-rainbow": true; }; }[]
kebabcaseKeys({ 123: 123 }); // $ExpectType { 123: number; }
kebabcaseKeys({ [symbol]: 'symbol' }); // $ExpectType {}
kebabcaseKeys({ camelCase: 'camelCase' }); // $ExpectType { "camel-case": string; }
kebabcaseKeys({ PascalCase: 'PascalCase' }); // $ExpectType { "pascal-case": string; }
kebabcaseKeys({ snake_case: 'snake_case' }); // $ExpectType { "snake-case": string; }
kebabcaseKeys({ 'kebabu-case': 'kebabu-case' }); // $ExpectType { "kebabu-case": string; }
kebabcaseKeys({ Hogehoge: new Date() }); // $ExpectType { hogehoge: Date; }
kebabcaseKeys({ UPPERCASE: 'UPPERCASE' }); // $ExpectType { uppercase: string; }
kebabcaseKeys({ lowercase: 'lowercase' }); // $ExpectType { lowercase: string; }
kebabcaseKeys({ point_class: point }); // $ExpectType { "point-class": Point; }
kebabcaseKeys({ person_interface: person }); // $ExpectType { "person-interface": Person; }
// @ts-expect-error
kebabcaseKeys(['']);

View File

@@ -0,0 +1,23 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"kebabcase-keys-tests.ts"
]
}

View File

@@ -0,0 +1 @@
{ "extends": "@definitelytyped/dtslint/dt.json" }