Language-tools: 支持 props 和响应式语句中的联合类型

创建于 2020-06-21  ·  28评论  ·  资料来源: sveltejs/language-tools

您的功能请求是否与问题有关?
由于打字稿的控制流分析,以下代码会出现打字稿错误。
但这对于选择包装器组件非常有用。

<script lang="typescript">
  export let value: string | number = '';

  if (typeof value === 'number') {
    console.log(value.toFixed())
  }

</script>

<select bind:value={value}></select>

描述您想要的解决方案
svelte2tsx中, props和响应式语句$: { }需要从控制流上下文中取出,可能需要用这样的回调包装它:

declare function __sveltets_invalidate<T>(getValue: () => T): T

/*export*/ let value: string | number = __sveltets_invalidate(() => '')

let b: 'A' | 'B' = 'A';

() => {
$: {
    console.log(b)
}
}

附加上下文
在此处添加有关功能请求的任何其他上下文或屏幕截图。
圖片

enhancement

所有28条评论

使用 invalidate-wrapper 围绕道具的好主意。 也许我们甚至不需要为响应式语句添加另一个包装器? 或者在任何情况下这会引发控制流错误?

export let更改为let并将语句包装在$: {}将有相同的错误

<script lang="typescript">
  let value: string | number = '';

  $: {
    if (typeof value === 'number') {
      console.log(value.toFixed())
    }
  }
</script>

如果值被事件处理程序重新分配。 它的类型可能会改变

啊,没想到。 谢谢!

道具的回调函数不起作用大声笑。 需要另寻出路。

顺便说一句,如果我们将第一个 $: 标签更改为 let ,就像我们之前在整个策略线程中讨论的那样,这种包装可以在 $: 变量中使用。 因为它的类型取决于另一个let 。 所以它会起作用。

另一个问题,这个union类型的props在导出的prop def中也会有一个字符串类型,因为同样的原因

export let someProps : string | number = ''

declare function __sveltets__invalidateWithDefault<T>(getValue: () => T | undefined): T;

let someProps: string | number = ''
someProps = __sveltets__invalidateWithDefault(() => someProps);

这行得通,但有点hacky

export let test: string | number = '' as any;

作品

@PatrickG的解决方案更好。 但是这两种方法都会使道具具有未定义的类型。

export let test: string | number | undefined = '' as any;
test // test is shouldn't be undefined here

你能以某种方式从 typescript AST 中获取类型并使用它来构造它吗? 或者使用某种 AST 遍历/正则表达式来获取类型定义(在:之后和=之前的所有内容),然后将其放在它后面: export let test: string | number = '' as string | number

@PatrickG的解决方案更好。 但是这两种方法都会使道具具有未定义的类型。

export let test: string | number | undefined = '' as any;
test // test is shouldn't be undefined here

对我来说测试是string | number 。 不是undefined
image

游乐场示例
如果strictNullChecks被启用,它将是undefinedstrict: true也启用它)

游乐场示例
如果strictNullChecks被启用,它将是undefinedstrict: true也启用它)

然后它将是string | number | undefined 。 问题出在哪里?

因为当您的默认值不是undefined $ 时,它不能是undefined #$ 。 我想消除undefined类型,以便用户不必进行不必要的空检查来抑制undefined错误。

如果您将| undefined添加到类型,当然它可以是未定义的,并且您应该在尝试调用方法时检查它是否未定义。

<!-- Component.svelte -->
<script lang="ts">
  export let test: string | undefined = '' as any; // `string | undefined` means it can be undefined.

  test.toUpperCase(); // not safe - because `test` can be `undefined`

  test?.toUpperCase(); // safe
</script>

<!-- Other component -->
<script>
  import Component from './Component.svelte';
</script>

<Componen test={undefined} />

我目前看到的唯一问题是,如果它有一个初始值,svelte 扩展应该使 prop 成为可选的。

我认为我们可以在这里使用VariableDeclaration.initializer而不是在此处检查类型中的undefined

因为 svelte 编译器将其编译为

let { test = '' } = $$props

所以 test 不能是undefined即使你明确地传递 undefined 作为道具。 如果您稍后将其重新分配给undefined ,它只能是未定义的。

也许做我打算做的事情太复杂或不可能。 它可能只是一个很少有人会使用的边缘案例。

如果您传递一个更改为未定义的变量,它仍然可以是未定义的。
https://svelte.dev/repl/hello-world?version=3.23.2

那么它的反应性声明可能是undefined ? 也许我们应该只做响应式并要求用户显式转换默认值的类型。

export let a: string | number | undefined = '' as string | number

铸造可以工作。 但是在js中它不会在函数中有未定义的类型

    /**<strong i="8">@type</strong> { string | number | undefined }*/
    let name = /**<strong i="9">@type</strong> {string | number} */ ("world"),
        world = '';

    name

    function a() {
        name
    }

js游乐场示例

我认为我们可以在这里使用 VariableDeclaration.initializer 而不是在这里检查类型中的 undefined

好点子! 它可以区分可选的道具和可能未定义的

这就是我的意思,带有可选道具: https ://github.com/PatrickG/language-tools/commit/c5f00f75df74c8e3dcfc9842dad4256b9d4f29f2

并且在初始化器后面加上as any ,vscode 不再认为export let test: string | number = '';是一个字符串。 https://github.com/PatrickG/language-tools/commit/7f630bbf7d66a037590384d3e2a7f8b7314b622c

我可以用我的更改创建 PR 吗?

我觉得第一个还行!
as any的另一个缺点是它不会进行类型检查。 因此,也许它需要转换为用户定义的任何内容。

let a: string | number = true as any;

另外,需要通过 js 中的jsdoc来进行强制转换。 我今天试过了。
关于未定义的问题,我放弃了。 因为我们找不到可以解决问题的通用函数。 我们只能手动检查其初始化程序是否未定义,但不能手动检查作为初始化程序传入的变量是否未定义。 假阴性似乎更重要。

我觉得第一个还行!
as any的另一个缺点是它不会进行类型检查。 因此,也许它需要转换为用户定义的任何内容。

let a: string | number = true as any;

啊对。
那这个呢:

declare function __sveltets_initialize<T>(value: T): any;
let test: string | number = __sveltets_initialize<string | number>('');
test; // string | number

let test: string | number = __sveltets_initialize<string | number>(true); // error because `true` is not `string` or `number`

另外,需要通过 js 中的jsdoc来进行强制转换。 我今天试过了。

我还没有研究过 jsdoc 的东西

更新:还有另一种方法可以做到as any

export let a: number | string = '1',
  b = a;

declare function __sveltets_any(dummy: any): any;

let a: number | string = '1'
a = __sveltets_any(a);
let b = a;

不需要类型断言,并且初始化程序得到了类型检查。 这样会显示输入错误:

export let a: ISomeInterface = {};
export let b: string | number = true;

我也想过重新分配,但想阻止它,因为我认为它更难做^^
你实施这个吗?

是的。 我在这里实现了重新分配,但仍需要一些清理和测试工作。
另一个是关注 $: 变量

@jasonlyu123问题是您的 PR 解决的所有问题吗? (询问我是否可以关闭这个)

是的。 谢谢

此页面是否有帮助?
0 / 5 - 0 等级