Typescript:具有自动完成功能的模型驱动和类型安全规范



我发现了这个$SPEC标记的模板文字原型

function $SPEC<Keys extends string[]>([result, ...parts]: TemplateStringsArray, ...keys: Keys) {

return (params: { [K in Keys[number]]: string }) => 

keys.reduce((acc, key: keyof typeof params, i) => acc + params[key] + parts[i], result)

}

它有可能";内插";降价中的规范-使用${Data}

const TestScenario = $SPEC `
# Feature XY 
>> As ${`role`} I want XY because of benefit ...
Scenario:
- given ${`Entity 1`} ...
- when ...
- then ...

`

到目前为止;插值驱动的";规格是";不知怎的";键入safe并带有自动完成功能-但仅适用于Data

const TestCase  = TestScenario({ // Test `Data`
'role': `Editor`,
'Entity 1': `example 1`
})

当前类型的安全和自动完成

打字游戏场

我如何才能在Type safe和autocomplete上走得更远;模型驱动"?

目标:

  • 为一个分析师和测试人员的集体(具有最低的编码技能(
  • 我正在寻找MINIMAListics TYPESCRIPT语法和实现(见上面的原型(
  • 交叉$SPECification需要的语法应该是:(Domain(MODEL DRIVEN&TYPE SAFE(针对型号(&带intelligsense/AUTOCOMPLETE(生产力(

术语:

  • 分析师和测试人员规范似乎至少在以下方面有交集:
    • 领域术语-模型中的元素
    • 和场景-TestCase通常基于UseCase/UserStory中的场景+通过域示例添加TestData(根据模型(

概念+工作流示例:


// sorry - this is only pseudocode (I'm just starting with Typescript and I don't know the relevant syntax, so I'm introducing "alternative syntax" where possible :)

// 1. DomainModels.ts - via Typescript Interfaces - from @Analyst
type Attribute = {}; type Action = {}; // structure is not important for now :)
interface role {}
interface Repository1 {}
interface Entity2 {
'Simple att3': Attribute
'Complex att4': {
'Att5': Attribute
// ...
}
'Action N': Action
}

// 2. Specifications.md.ts - via markdown, wrapped in MINIMAList Typescript - from @Anlayst and @Tester also
// declare function $SPEC (...) ...
const UseCase =  $SPEC
const TestScenario = $SPEC
// ... probably irrelevant syntax 
const UC1 = UseCase<{role, Repository1, Entity2}> 
`
# vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv vvvvvvvvvvvvvvvvvvvvvvvvv
>> vvvvvvvv ${`role`} vvvvvvvvvvvvvvv vvvvvvvvvvvvvvvvvvvvvvv
- vv ${`Repository1`} vvvvvvvvvv ${`Entity2.Simple att3`} vvv
- vvvvvvv ${`Entity2.Action N`} vvvvvvvvvvvvv vvvvvvvvvvvvvvvv
`
// ... other syntax alternatives ?
// const TS2 = TestScenario<[role, Repository1, Entity2]> `...`
// const Scenario3 = $SPEC<role | Repository1 | Entity2> `...`
// ALL "syntaxes / alternatives" only TRY to "DELIVER" MODELS TO $SPEC
// ... or this alternative would be nice - has "shortcuts" for "models" :)
const Scenario4 = $SPEC( (r: role, rep: Repository1, e: Partial<Entity2>) => `
>> vvvvvvvv ${r} vvvvvvvvvvvvvvv vvvvvvvvvvvvvvvvvvvvvvv
- vv ${rep} vvvvvvvvvv ${e['Simple att3']} vvv
- vvvvvvv ${e['Action N']} vvvvvvvvvvvvv vvvvvvvvvvvvvvvv
` )

// 3. Examples.ts - via Typescript call of $SPEC() "aliases" - from @Tester
const TC4_1 = Scenario4( // again, exact structure of some parameters is not important for now :)
{}, // r:   role 
{}, // rep: repository
{   // e:   Partial<Entity2>
'Simple att3': 'example 1',
'Action N': () => {}
}
)

所以,请你为给定的概念建议你的迷你打字语法(以及可能的实现(???

或者你知道一些像这样的框架/库/工具吗;型号驱动型安全规范???(我知道Cucumber和Gauge,它们很接近,但不是我想要的(

如果有任何建议或意见,我将不胜感激,非常欢迎。

谢谢你(如果你读到这一点——我无法剪切:(

多亏了@jcalz,才有了这个解决方案:


type _K2O<K extends string, V = unknown> =
K extends `${infer F}.${infer R}` ? { [P in F]: _K2O<R, V> } : { [P in K]: V }
type DeepMerge<T> = { [K in keyof T]: DeepMerge<T[K]> } extends infer O ? { [K in keyof O]: O[K] } : never;
type KeyToObject<K extends string, V = unknown> = (K extends unknown ? (x: _K2O<K, V>) => void : never) extends
(x: infer O) => void ? DeepMerge<O> : never;
type DotPrefix<T extends string> = T extends "" ? "" : `.${T}`
type DotNestedKeys<T> = (T extends object ?
{ [K in Exclude<keyof T, symbol>]: `${K}${DotPrefix<DotNestedKeys<T[K]>>}` }[Exclude<keyof T, symbol>]
: "") extends infer D ? Extract<D, string> : ''; // here I intuitively changed : never to : '', because of problem with next split()
function $SPEC<M>() {
return <Keys extends DotNestedKeys<M>[]>([result, ...parts]: TemplateStringsArray, ...keys: Keys) => {
return (params: KeyToObject<Keys[number], string>): string =>
keys.reduce((acc, key, i) => acc + (key.split(".").reduce<any>((o, k) => o[k], params)) + parts[i], result)
}
}

// 1. DomainModels.ts
interface Entity2 {
'Simple att3': string
'Complex att4': {
'Att5': string
// ...
}
'Action N': string
}
// 2. Specifications.md.ts
const TestScenario = $SPEC;
const TS1 = TestScenario<{role: string, entity2: Entity2}>()
`
# vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv vvvvvvvvvvvvvvvvvvvvvvvvv
>> vvvvvvvv ${`role`} vvvvvvvvvvvvvvv vvvvvvvvvvvvvvvvvvvvvvv
- vvvvvvvvvvvv ${`entity2.Simple att3`} vvvvvvvvvvvvvv
- vvvvvvv ${`entity2.Action N`} vvvvvvvvvvvvv vvvvvvvvvvvvvvvv
`
// 3. Examples.ts
TS1({ // TestCase aka Example = [TestScenario, TestData]
role: `Editor`,
entity2: {
'Simple att3': `example 1`,
'Action N': `exmple 2`
}
})

$SPEC resp。分别测试场景。TS1现在也有自动完成功能,是一个接口,也就是模型驱动的

最新更新