Javascript 将函数转换为使用typescript类型
我的职责如下:Javascript 将函数转换为使用typescript类型,javascript,typescript,Javascript,Typescript,我的职责如下: const selector = (definitions, query = {}) => { const select = {}; Object.keys(definitions).forEach((key) => { if (typeof query[key] !== 'undefined') { if (definitions[key].validation(query[key])) { if (typeof def
const selector = (definitions, query = {}) => {
const select = {};
Object.keys(definitions).forEach((key) => {
if (typeof query[key] !== 'undefined') {
if (definitions[key].validation(query[key])) {
if (typeof definitions[key].convert !== 'undefined') {
select[key] = definitions[key].convert(query[key], key);
} else {
select[key] = query[key];
}
} else if (typeof definitions[key].default !== 'undefined') {
select[key] = definitions[key].default;
}
} else if (typeof definitions[key].default !== 'undefined') {
select[key] = definitions[key].default;
}
});
return select;
};
这个类型脚本报告我需要正确地键入定义
和查询
,但我真的不知道如何键入
错误消息。
元素隐式具有“any”类型,因为类型“{}”没有索引签名。
我怎么做
用法:
const-config={
姓名:{
验证:val=>val.length>=3
},
第页:{
验证:val=>Number.isInteger(parseInt(val,10)),
默认值:1,
},
限制:{
默认值:10,
}
}
常量myQuery={
姓名:‘da’,
第页:'不可解开',
另一个:"1",,
}
选择(配置,myQuery)
试验
const expected=JSON.stringify({
页码:1,
限额:10,
})
const output=JSON.stringify(选择器(config,myQuery))
如果(预期!==输出){
抛出新错误(`输出错误。\n预期:${expected}\n输出:${output}`)
}
使用情况:
注意:
验证
是必需的,但转换
和默认
是可选参数。索引签名是指当您指定对象的各个键将对应于某一类型时,即使您事先不知道这些键将是(或不想枚举它们)哪些键
例如,您的definitions参数可能定义如下:
interface definition {
validation?: (arg: any) => boolean;
convert?: (arg0: any, arg1: any) => any;
default?: number;
}
interface definitionsCollection {
[key: string]: definition; // <--- this is an index signature
}
const selector = (definitions: definitionsCollection, query = {}) => {
// ... etc
}
为定义
参数提供了一个很好的解决方案。我们可以对其进行一些增强,使其更为通用和明确,并为查询
参数提供一个类型:
type QueryValue = any;
type Query = { [key: string]: QueryValue };
type Definition<T1, T2=T1> = {
validation?: (value: QueryValue) => boolean;
convert ?: (value: QueryValue, key: string) => T1;
default ?: T2;
};
我们可以使用一些可以在编辑器中评估的“测试类型”来验证SelectorResult
类型。结果很好,但如果合并了交点类型,效果会更好:
type Test1 = SelectorResult<{ a: 'invalid' }>;
// Not a `Definition<T>`
// => Expected `{}`
// >> Received `{} & {}` : OK
type Test2 = SelectorResult<{ a: any }>;
// ~`Definition<unknown>`
// => Expected `{ a?: unknown }`
// >> Received `{ a: {} } & { a?: unknown }` : ~OK
type Test3 = SelectorResult<{ a: WithConvert<any> }>;
// `convert` alone is ignored.
// => Expected `{}`
// >> Received `{} & {}` : OK
type Test4 = SelectorResult<{ a: WithDefault<number> }>;
// `default` ensures the key is kept and provides the value type: `number`.
// => Expected `{ a: number }`
// >> Received `{ a: number } & {}` : OK
type Test5 = SelectorResult<{ a: WithDefault<number> & WithConvert<any> }>;
// Idem Test 4: `convert()` still ignored, only `default` matters.
// => Expected `{ a: number }`
// >> Received `{ a: number } & {}` : OK
type Test6 = SelectorResult<{ a: WithValidation }>;
// `validation()` alone makes the key optional and the value type `unknown`.
// => Expected `{ a?: unknown }`
// >> Received `{} & { a?: unknown }` : OK
type Test7 = SelectorResult<{ a: WithValidation & WithConvert<string> }>;
// Idem Test6 + `convert()` providing the value type: `string`
// => Expected `{ a?: string }`
// >> Received `{} & { a?: string | undefined }` : OK
type Test8 = SelectorResult<{ a: WithDefault<number>; b: 'invalid'; c: WithValidation }>;;
// Combining several keys from different cases: a (Test4), b (Test1), c (Test6)
// => Expected `{ a: number; c?: unknown }`
// >> Received `{ a: number } & { c?: unknown }` : OK
type Test1=SelectorResult;
//不是`定义`
//=>应为`{}`
//>>收到`{}&{}`:OK
类型Test2=SelectorResult;
//~`定义`
//=>应为{a?:未知}`
//>>收到`{a:{}}&{a?:未知}`:~确定
type Test3=SelectorResulttypescript typings
tag用于与第三方库的拉入类型定义相关的问题。不清楚,但这个问题似乎不需要这个标签。哦,谢谢你这么说,我将删除它。你能提供一个或多个用例示例plz吗<代码>常量结果=选择器({tbd},{tbd});预期(结果)。toEqual(待定)代码>。因此,可以为输入参数定义
和查询
以及输出设置适当的类型。然后Nicholas的答案可以适合其他更通用的解决方案(不仅仅是字符串)。基于此,我添加了我自己的回答,虽然不完全准确,但我认为仍然很有趣。键入(any)=>boolean
也将生成隐式'any'
错误,它应该是(arg:any)=>boolean
(或者更好的做法是给参数指定有意义的名称,而不是arg
)。感谢您的更正。我已经将它更新为(arg:any)
,尽管我同意他们应该给它更多有意义的名称和类型(我只是不知道这些名称和类型是什么)。我还有一个问题,请检查这个示例@NicholasTower@DavidCosta我的答案是在你们添加数据示例之前给出的。所以我不得不猜一些类型,我猜错了。我已经更新了答案,将默认设置为一个数字,并将所有属性设置为可选。
type WithValidation = { validation: (value: QueryValue) => boolean; }
type WithConvert<T> = { convert : (value: QueryValue, key: string) => T; }
type WithDefault<T> = { default : T; }
type SelectorResult<D> = {
[P in {
[K in keyof D]:
D[K] extends WithDefault<any> ? K : never
}[keyof D]]:
D[P] extends WithDefault<infer T> ? T : never;
} & {
[P in {
[K in keyof D]:
D[K] extends WithDefault<any> ? never :
D[K] extends WithConvert<any> ?
D[K] extends WithValidation ? K : never :
D[K] extends Definition<any> ? K : never
}[keyof D]]?:
D[P] extends WithValidation & WithConvert<infer T> ? T :
D[P] extends Definition<any> ? unknown : never;
};
type Test1 = SelectorResult<{ a: 'invalid' }>;
// Not a `Definition<T>`
// => Expected `{}`
// >> Received `{} & {}` : OK
type Test2 = SelectorResult<{ a: any }>;
// ~`Definition<unknown>`
// => Expected `{ a?: unknown }`
// >> Received `{ a: {} } & { a?: unknown }` : ~OK
type Test3 = SelectorResult<{ a: WithConvert<any> }>;
// `convert` alone is ignored.
// => Expected `{}`
// >> Received `{} & {}` : OK
type Test4 = SelectorResult<{ a: WithDefault<number> }>;
// `default` ensures the key is kept and provides the value type: `number`.
// => Expected `{ a: number }`
// >> Received `{ a: number } & {}` : OK
type Test5 = SelectorResult<{ a: WithDefault<number> & WithConvert<any> }>;
// Idem Test 4: `convert()` still ignored, only `default` matters.
// => Expected `{ a: number }`
// >> Received `{ a: number } & {}` : OK
type Test6 = SelectorResult<{ a: WithValidation }>;
// `validation()` alone makes the key optional and the value type `unknown`.
// => Expected `{ a?: unknown }`
// >> Received `{} & { a?: unknown }` : OK
type Test7 = SelectorResult<{ a: WithValidation & WithConvert<string> }>;
// Idem Test6 + `convert()` providing the value type: `string`
// => Expected `{ a?: string }`
// >> Received `{} & { a?: string | undefined }` : OK
type Test8 = SelectorResult<{ a: WithDefault<number>; b: 'invalid'; c: WithValidation }>;;
// Combining several keys from different cases: a (Test4), b (Test1), c (Test6)
// => Expected `{ a: number; c?: unknown }`
// >> Received `{ a: number } & { c?: unknown }` : OK
const keySelector = <D, Q>(definitions: D, query: Q, key: string) => {
const val = query[key as keyof Q];
const def = definitions[key as keyof D] as Definition<D[keyof D]>;
if (typeof val !== 'undefined' &&
typeof def.validation !== 'undefined' &&
def.validation(val)) {
return typeof def.convert !== 'undefined'
? { [key]: def.convert(val, key) }
: { [key]: val };
}
return typeof def.default !== 'undefined'
? { [key]: def.default }
: {};
};
const safeSelector = <D>(definitions: D, query: Query) =>
Object.keys(definitions).reduce(
(result, key) => Object.assign(result, keySelector(definitions, query, key)),
{} as SelectorResult<D>);
const selector = <D>(definitions: D, query: Query = {}) =>
safeSelector(definitions, query || {});
// Tests (with colors for Chrome/Firefox JavaScript Console)
test('Skip key not defined', {}, { a: 1 }, {});
test('Empty definition', { a: {} }, { a: 1 }, {});
test('Use default when key is missing', { a: { default: 10 } }, {}, { a: 10 });
test('Keep key with valid definitions', { a: { default: 10 }, b: { ko: 'def' } }, {}, { a: 10 });
test('Use default when value is not valid ', { a: { default: 10, validation: () => false } }, { a: 1 }, { a: 10 });
test('Use value when it is valid ', { a: { default: 10, validation: () => true } }, { a: 1 }, { a: 1 });
test('Use converted value when it is valid', { a: { convert: (a: number, k: string) => `${k}=${a * 2}`, validation: () => true } }, { a: 1 }, { a: 'a=2' });
function test<D>(message: string, definitions: D, query: Query, expected: SelectorResult<D>) {
const actual = selector(definitions, query);
const args = [expected, actual].map(x => JSON.stringify(x));
if (args[0] === args[1]) {
console.log(`%c ✔️ ${message}`, 'color: Green; background: #EFE;');
} else {
console.log(`%c ❌ ${message} KO : Expected ${args[0]}, Received ${args[1]}`, 'color: Red; background: #FEE;');
}
}