我创建了一个配置对象检查器函数,它基本上检查对象是否与蓝图匹配。与React道具类型的工作原理非常相似。我将使用它在不同的网站上使用不同的配置文件自动部署应用程序,以确保在尝试部署之前正确定义配置文件。
我有一个函数,它接受一个对象并返回一个函数。
const blueprint = {
stringValue: ConfigTypes.string,
requirednumberValue: ConfigTypes.number.isRequired,
boolOrStringValue: ConfigTypes.oneOfType([ConfigTypes.string, ConfigType.bool])
} //The syntax here is very similar to that of React Prop Types. I am essentially defining what I expect my object to look like.
const checker = ConfigChecker(blueprint)
ConfigChecker
获取一个蓝图,说明我们期望对象中的键是什么,指定键的值类型是什么,以及它们是可选的还是必需的。ConfigChecker
返回一个函数,该函数以两个对象为参数。
const config = {
stringValue: "Hello"
boolOrStringValue: true
}
const defaults = {
requirednumberValue: 5,
boolOrStringValue: false
}
checker(config, defaults) //checker is defined in the above example. The return of ConfigChecker(blueprint)
config
参数是我们计划用于应用的配置对象,而默认值
参数是我们可以用于应用的默认键值对,如果它们没有在配置对象中指定。
在内部,config
参数和defaults
参数与覆盖defaults对象中相同键值的配置对象深度合并在一起。
因此,上述示例的结果将是:
{
stringValue: "Hello"
boolOrStringValue: true
requirednumberValue: 5,
}
合并两个参数后,将根据blueprint
对它们进行测试,以确保正确定义包含合并default
和config
对象的最终配置对象。
此函数的声明文件如下所示:
interface Requireable {}
interface ConfigType {
isRequired: Requireable;
}
type CType = {
/**
* A string value.
*/
string: ConfigType;
/**
* A boolean value.
*/
bool: ConfigType;
/**
* A number value.
*/
number: ConfigType;
/**
* A function.
*/
func: ConfigType;
/**
* An object. Not an array.
*/
object: ConfigType;
/**
* An array.
*/
array: ConfigType;
/**
* Any value.
*/
any: ConfigType;
/**
* An array of a specific type.
*/
arrayOf: (type: CType[keyof CType]) => ConfigType;
/**
* An object containing a specific type.
*/
objectOfType: (type: CType[keyof CType]) => ConfigType;
/**
* One of these values.
* @example
* OneOf(["Hello", "Goodbye", false])
*/
oneOf: (enums: Array<any>) => ConfigType;
/**
* One of these types.
* @example
* OneOf([ConfigType.string, ConfigType.number])
*/
oneOfType: (types: Array<CType[keyof CType]>) => ConfigType;
/**
* An object with specific keys and value types.
*/
objectOf: (obj: { [key: string]: CType[keyof CType] }) => ConfigType;
/**
* An object with specific keys and value types. The objects must strictly match.
*/
exactObjectOf: (obj: { [key: string]: CType[keyof CType] }) => ConfigType;
};
/**
* Check functions.
*/
export const ConfigTypes: CType;
type Id<T> = { [K in keyof T]: T[K] };
type SpreadProperties<L, R, K extends keyof L & keyof R> = {
[P in K]: L[P] | Exclude<R[P], undefined>;
};
type OptionalPropertyNames<T> = {
[K in keyof T]: undefined extends T[K] ? K : never;
}[keyof T];
type Spread<L, R> = Id<
// Properties in L that don't exist in R
Pick<L, Exclude<keyof L, keyof R>> &
// Properties in R with types that exclude undefined
Pick<R, Exclude<keyof R, OptionalPropertyNames<R>>> &
// Properties in R, with types that include undefined, that don't exist in L
Pick<R, Exclude<OptionalPropertyNames<R>, keyof L>> &
// Properties in R, with types that include undefined, that exist in L
SpreadProperties<L, R, OptionalPropertyNames<R> & keyof L>
>;
export default function <S extends { [key: string]: CType[keyof CType] }>(
schema: S
): <C extends { [key: string]: any }, D extends { [key: string]: any }>(
config: C,
defaults?: D
) => Pick<Spread<C, D>, keyof S>;
本质上,这个声明文件声明的是调用ConfigChecker(蓝图)(配置,默认值)
的结果值应该是一个对象,该对象仅包含蓝图
对象中的键,这些键的值类型来自合并的配置
和默认值
对象。因此,如果我要向config
对象添加一个键,而不首先定义蓝图
对象中的键,返回的对象将不包含添加的键;只有蓝图
对象中的键将在返回的对象中定义。
虽然这很有效,但它只是我真正想做的一个变通方法。
我真正想从我的申报文件中实现的是:
给定上面的蓝图
,typecript应该推断返回的对象,在提供config
和默认值
对象之前,应该是这样的:
{
stringValue?: string,
requirednumberValue: number,
boolOrStringValue?: boolean | string
}
然后,在添加了config
和defaults
对象之后,typescript应该将它们交叉引用到蓝图,并返回如下内容:
{
stringValue: string,
requirednumberValue: number,
boolOrStringValue: boolean
}
所以首先,TypeScript应该推断函数的返回类型应该是一个包含可选和必需键/值对的对象。这些键/值对可以有多种类型,就像:boolOrStringValue?: boolean|string
一样,它既是可选的,也可以是布尔值或字符串。
最后,TypeScript应该读取合并的config
和默认值
对象中的值,并替换已知类型的假设类型。
我知道第一个解决办法很好,但是,除了这是一个很酷的功能外,它还允许我在打字脚本上绞尽脑汁,因为我才刚刚开始。我喜欢做这样的事,把自己弄得筋疲力尽。像这样的事情可以做吗?或者我是在我的头上?
提前谢谢。
我设法找到了自己的解决办法。这是新的声明文件:
interface OConfigType<T> {
isRequired: RConfigType<T>;
}
interface RConfigType<_T> {
(): void;
}
type GetType<T> = T extends OConfigType<infer T> | RConfigType<infer T>
? T
: never;
type CType = {
/**
* A string value.
*/
string: OConfigType<string>;
/**
* A boolean value.
*/
bool: OConfigType<boolean>;
/**
* A number value.
*/
number: OConfigType<number>;
/**
* A function.
*/
func: OConfigType<Function>;
/**
* An object. Not an array.
*/
object: OConfigType<Object>;
/**
* An array.
*/
array: OConfigType<Array<any>>;
/**
* Any value.
*/
any: OConfigType<any>;
/**
* An array of a specific type.
*/
arrayOf: <S>(type: S) => OConfigType<Array<GetType<S>>>;
/**
* An object containing a specific type.
*/
objectOfType: <S>(
type: S
) => OConfigType<Record<string | number, GetType<S>>>;
/**
* One of these values.
* @example
* OneOf(["Hello", "Goodbye", false])
*/
oneOf: <T>(
enums: [...T]
) => T extends Array<infer U> ? OConfigType<U> : never;
/**
* One of these types.
* @example
* OneOf([ConfigType.string, ConfigType.number])
*/
oneOfType: <T>(types: [...T]) => T extends Array<infer U> ? U : never;
/**
* An object with specific keys and value types.
*/
objectOf: <S extends { [key: string | number]: OConfigType | RConfigType }>(
obj: S
) => OConfigType<ReturnSchema<S>>;
/**
* An object with specific keys and value types. The objects must strictly match.
*/
exactObjectOf: <
S extends { [key: string | number]: OConfigType | RConfigType }
>(
obj: S
) => OConfigType<ReturnSchema<S>>;
};
/**
* Check functions.
*/
export const ConfigTypes: CType;
type Id<T> = { [K in keyof T]: T[K] };
type SpreadProperties<L, R, K extends keyof L & keyof R> = {
[P in K]: L[P] | Exclude<R[P], undefined>;
};
type OptionalPropertyNames<T> = {
[K in keyof T]: undefined extends T[K] ? K : never;
}[keyof T];
type Spread<L, R> = Id<
// Properties in L that don't exist in R
Pick<L, Exclude<keyof L, keyof R>> &
// Properties in R with types that exclude undefined
Pick<R, Exclude<keyof R, OptionalPropertyNames<R>>> &
// Properties in R, with types that include undefined, that don't exist in L
Pick<R, Exclude<OptionalPropertyNames<R>, keyof L>> &
// Properties in R, with types that include undefined, that exist in L
SpreadProperties<L, R, OptionalPropertyNames<R> & keyof L>
>;
// export default function <S extends { [key: string]: CType[keyof CType] }>(
// schema: S
// ): <C extends { [key: string]: any }, D extends { [key: string]: any }>(
// config: C,
// defaults?: D
// ) => Pick<Spread<C, D>, keyof S>;
//type ReturnType<S> =
type Filter<Base, Condition> = {
[Key in keyof Base]: Base[Key] extends Condition ? Key : never;
}[keyof Base];
type FilterReturnType<S> = {
[Key in keyof S]: S[Key] extends OConfigType | RConfigType ? Key : never;
}[keyof S];
type GetReturnType<S> = {
[Key in keyof S]: S[Key] extends OConfigType<infer T> | RConfigType<infer T>
? T
: never;
};
type ReturnType<S> = GetReturnType<Pick<S, FilterReturnType<S>>>;
type ReturnSchema<S> = ReturnType<Pick<S, Filter<S, RConfigType<any>>>> &
ReturnType<Partial<Pick<S, Filter<S, OConfigType<any>>>>>;
type ReturnConfig<S, C> = {
[K in keyof S]: C[K];
};
interface ReturnFunction<S> {
<C extends S, D extends S>(config: C, defaults?: D = {}): ReturnConfig<
S,
Spread<C, D>
>;
}
export default function <
S extends {
[key: string | number]: OConfigType | RConfigType;
}
>(schema: S): ReturnFunction<ReturnSchema<S>>;
除了读取config
和defaults
的值以更新最终对象之外,这完成了我试图实现的大部分功能。但至少现在函数的输出是基于您输入的blueprint
对象的。
现在,正确定义了config
和defaults
对象的预期输入:
并且正确定义了输出对象。
TypeScript中有些独特的概念可以在类型层面上描述JavaScript对象的模型。 这其中尤其独特的一个例子是“声明合并”的概念。 理解了这个概念,将有助于操作现有的JavaScript代码。 同时,也会有助于理解更多高级抽象的概念。 对本文件来讲,“声明合并”是指编译器将针对同一个名字的两个独立声明合并为单一声明。 合并后的声明同时拥有原先两个声明的特性。 任何数量的声明都可被合并;不局限
TS 玩的顺溜不顺溜,就看你的 d.ts 文件写的溜不溜。 在学如何书写声明文件之前,我们先来看看声明相关的一些东西。 接口合并 当我们多次使用 interface 定义的时候,会合并接口 这里报错的原因是,我们并没有完全的实现 A 接口。 错误提示告诉我们,还有一个 age 属性没有。 假如你使用的 2.1 版本的 ts,那么你可以用 keyof 关键字拿到 A 的所有属性值类型。 命名空间的合
TypeScript有一些独特的概念,有的是因为我们需要描述JavaScript顶级对象的类型发生了哪些变化。 这其中之一叫做声明合并。 理解了这个概念,对于你使用TypeScript去操作现有的JavaScript来说是大有帮助的。 同时,也会有助于理解更多高级抽象的概念。 首先,在了解如何进行声明合并之前,让我们先看一下什么叫做声明合并。 在这个手册里,声明合并是指编译器会把两个相同名字的声明
问题内容: 有人可以解释一下这是什么意思吗? 这似乎是一个循环定义,至少可以说让我感到困惑。 问题答案: Java Generics FAQ中 有很好的解释。 从末尾开始: 概括起来,声明可以解密为:是仅可为其子类型实例化的泛型类型,并且这些子类型将继承一些有用的方法,其中一些方法具有子类型特定的参数(否则取决于子类型)。 (尽管我确实很同情-递归的泛型声明很痛苦。但是,我的协议缓冲端口到C#的情
问题内容: 注意:纯粹出于好奇而不是用于任何实际用例。 我想知道是否有一种方法可以 使用有效的类型参数声明对象: 如果和是可以互换的,为什么和不呢? 编辑: 问题可以推广到嵌套原始类型参数的问题。例如: 编辑2: 我应该稍微改一下这个问题:我知道 是有效的,但我想知道为什么与 问题答案: 泛型有一些相当严重的局限性。在这种情况下,您不能将类型分配给内部类型,因为您实际上是在引用原始类型,而不是原始
本文向大家介绍如何使用JSP声明来声明类的对象?,包括了如何使用JSP声明来声明类的对象?的使用技巧和注意事项,需要的朋友参考一下 声明声明了一个或多个变量或方法,您可以稍后在JSP文件中的Java代码中使用它们。在JSP文件中使用变量或方法之前,必须先声明该变量或方法。 以下是JSP声明的语法- 您可以编写与上述语法等效的XML,如下所示- 以下是JSP声明中对象声明的示例-