首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Typescript允许破坏类型安全的突变吗?

Typescript允许破坏类型安全的突变吗?
EN

Stack Overflow用户
提问于 2021-10-29 10:39:21
回答 1查看 67关注 0票数 2

在下面的代码片段中,是否证明了Typescript不能警告严格类型的对象以一种违反其类型约束的方式发生突变?

代码语言:javascript
复制
type Animal = {
  name: 'cat'
  noise: 'meow'
} | {
  name: 'dog'
  noise: 'woof'
};


const f = (animal: Animal) => {
  // Why is this allowed? Typescript still considers this to be of type 'Animal'
  // after this mutation, even though its value no longer assignable to any variant
  // of Animal.
  animal.noise = 'woof';

  // ❌ - Typescript failed to update animal's type when we mutated it, so now
  // it mistakenly continues to believe it is assignable to Animal everywhere!
  // Now its incorrectly typed data can spread throughout the application,
  // breaking type safety, potentially everywhere...
  const anotherAnimal: Animal = animal;

  // ✅ - Typescript correctly prevents us from assigning this combination of fields
  // to type Animal. But this is exactly the value we mutated to in our first example,
  // so why were we allowed to do that when the outcome is the same, invalid result?
  const animalMatchingPostMutationValue: Animal = {
    name: 'cat',
    noise: 'woof'
  }
}

const animal: Animal = { 
  name: 'cat',
  noise: 'meow',
}

f(animal)

请参见操场链接here

我错误地认为应该防止这种情况发生吗?如果不是,除了简单地避免突变(也许使用readonly来强制执行此实践)之外,还有其他方法吗?

编辑

修改了示例,以避免强制转换。

EN

回答 1

Stack Overflow用户

发布于 2021-10-29 10:48:17

不应在中使用as Animal

代码语言:javascript
复制
const animal: Animal = { 
  name: 'cat',
  noise: 'meow',
} as Animal

它没有任何意义,会使您代码变得unsafe

请参阅docs

类型断言是一种告诉编译器“相信我,我知道我在做什么”的方法。类型断言类似于其他语言中的类型转换,但它不执行特殊的数据检查或重新构造。它没有运行时影响,仅供编译器使用。TypeScript假定您(程序员)已经执行了所需的任何特殊检查。

一旦您编写了as Animal,TypeScript就假定任何Animal都可以分配给相应的属性:

代码语言:javascript
复制
const animal: Animal = {
  name: 'cat',
  noise: 'meow',
} as Animal

animal.name // "cat" | "dog"
animal.noise // "meow" | "woof"

它不再能够区分它是否是联合的一部分。

代码语言:javascript
复制
type Name = Animal['name'] // "cat" | "dog"
type Noise = Animal['noise'] // "meow" | "woof"

这就是为什么类型断言是不安全的,只有在没有其他选择的情况下才应该考虑。

突变在typescript中是有点不安全的操作,因为除了一个case之外,TS不会跟踪它们。

你可以在my article中找到更多关于typescript突变的信息

TL;DR

只是试图避免typescript中的突变

与typescript中的突变相关的答案列表

How to selectively assign from one Partial to another in typescript

Why can I index by string to get a property value but not to set it?

TypeScript: Why can't I assign a valid field of an object with type { a: "a", b: "b" }

Why does Typescript say this variable is "referenced directly or indirectly in its own initializer"?

https://github.com/microsoft/TypeScript/pull/30769

keyof and React Redux action

Cannot dynamically set interface object's property values

Convert Object.entries reduce to Generics type

摘要主要问题是对象可变属性是协变的。COnsider此示例是从Titian-Cernicova-Dragomir 'stalk窃取的

代码语言:javascript
复制
type Type = {
    name: string
}

type SubTypeA = Type & {
    salary: string
}

type SubTypeB = Type & {
    car: boolean
}

let employee: SubTypeA = {
    name: 'John Doe',
    salary: '1000$'
}

let human: Type = {
    name: 'Morgan Freeman'
}

let student: SubTypeB = {
    name: 'Will',
    car: true
}


// same direction
type Covariance<T> = {
    box: T
}

let employeeInBox: Covariance<SubTypeA> = {
    box: employee
}

let humanInBox: Covariance<Type> = {
    box: human
}

/**
 * MUTATION 
 */
let test: Covariance<Type> = employeeInBox

test.box = student // mutation of employeeInBox

const result_0 = employeeInBox.box.salary // while result_0 is undefined, it is infered a a string

console.log({ result_0 })

result_0undefined,而TS认为它是string。为了解决这个问题,只需对COvariance类型使用readonly标志:

代码语言:javascript
复制
type Covariance<T> = {
    readonly box: T
}

现在它失败了,因为它应该是这样的。

没有 readonly解决方法

代码语言:javascript
复制
type Cat = {
  name: 'cat'
  noise: 'meow'
}

type Dog = {
  name: 'dog'
  noise: 'woof'
};

type Animal<Name, Noise> = { name: Name, noise: Noise }

const f = <
  CatName extends Cat['name'],
  CatNoise extends Cat['noise'],
  DogName extends Dog['name'],
  DogNoise extends Dog['noise'],
  Pet extends Animal<CatName, CatNoise> | Animal<DogName, DogNoise>
>(animal: Pet) => {

  animal.noise = 'woof'; // error

  const anotherAnimal: Pet = animal;

  // error
  const animalMatchingPostMutationValue: Animal = {
    name: 'cat',
    noise: 'woof'
  }
}
const animal: Animal = {
  name: 'cat',
  noise: 'meow',
}

f(animal) // ok

Playground,你可以把它变成逆变量。看起来不太好,但很管用。

您可以使用eslint插件来强制执行readonly标志

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69767493

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档