惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
K
Kaspersky official blog
Apple Machine Learning Research
Apple Machine Learning Research
B
Blog
aimingoo的专栏
aimingoo的专栏
M
MIT News - Artificial intelligence
小众软件
小众软件
云风的 BLOG
云风的 BLOG
腾讯CDC
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Hugging Face - Blog
Hugging Face - Blog
S
SegmentFault 最新的问题
Stack Overflow Blog
Stack Overflow Blog
量子位
S
Secure Thoughts
G
GRAHAM CLULEY
C
CXSECURITY Database RSS Feed - CXSecurity.com
人人都是产品经理
人人都是产品经理
雷峰网
雷峰网
T
Threat Research - Cisco Blogs
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Cisco Talos Blog
Cisco Talos Blog
G
Google Developers Blog
爱范儿
爱范儿
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
有赞技术团队
有赞技术团队
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Martin Fowler
Martin Fowler
The GitHub Blog
The GitHub Blog
Google DeepMind News
Google DeepMind News
C
Cisco Blogs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
博客园 - 聂微东
宝玉的分享
宝玉的分享
H
Hackread – Cybersecurity News, Data Breaches, AI and More
N
Netflix TechBlog - Medium
Forbes - Security
Forbes - Security
Engineering at Meta
Engineering at Meta
S
Security Affairs
Help Net Security
Help Net Security
博客园 - 三生石上(FineUI控件)
AWS News Blog
AWS News Blog
博客园 - 叶小钗
Recent Commits to openclaw:main
Recent Commits to openclaw:main
V2EX - 技术
V2EX - 技术
Hacker News: Ask HN
Hacker News: Ask HN
Project Zero
Project Zero
H
Heimdal Security Blog
W
WeLiveSecurity
C
Check Point Blog

oida.dev | TypeScript, Rust

TypeScript's `erasableSyntaxOnly` Flag Unsafe for work Tokio: Macros Tokio: Channels Tokio: Getting Started Network Applications on the Tokio Stack Remake, Remodel, Reduce. The `never` type and error handling in TypeScript 5 Inconvenient Truths about TypeScript Refactoring in Rust: Introducing Traits Refactoring in Rust: Abstraction with the Newtype Pattern Announcing the TypeScript Cookbook The road to universal JavaScript 10 years of oida.dev Rust: Tiny little traits The TypeScript converging point How not to learn TypeScript Getting started with Rust Introducing Slides and Coverage TypeScript: The humble function overload TypeScript + React: Children types are broken TypeScript: In defense of any Rust: Enums to wrap multiple errors Dissecting Deno Error handling in Rust TypeScript: Unexpected intersections Upgrading Node.js dependencies after a yarn audit TypeScript: Array.includes on narrow types TypeScript + React: Typing Generic forwardRefs shared, util, core: Schroedinger's module names Learning Rust and Go TypeScript: Narrow types in catch clauses TypeScript: Low maintenance types Tidy TypeScript: Name your generics Tidy TypeScript: Avoid traditional OOP patterns Tidy TypeScript: Prefer type aliases over interfaces Tidy TypeScript: Prefer union types over enums My new book: TypeScript in 50 Lessons Go Preact! ❤️ this in JavaScript and TypeScript TypeScript and ECMAScript Modules TypeScript + React: Why I don't use React.FC TypeScript + React: Component patterns TypeScript: Augmenting global and lib.dom.d.ts Vite with Preact and TypeScript TypeScript: Union to intersection type 11ty: Generate Twitter cards automatically Are large node module dependencies an issue? TypeScript: Variadic Tuple Types Preview TypeScript: Improving Object.keys Remake, Remodel. Part 4. TypeScript + React: Typing custom hooks with tuple types TypeScript: Assertion signatures and Object.defineProperty TypeScript: Check for object properties and narrow down type Boolean in JavaScript and TypeScript void in JavaScript and TypeScript Symbols in JavaScript and TypeScript Why I use TypeScript TypeScript + React: Extending JSX Elements TypeScript: Validate mapped types and const context TypeScript: Match the exact object shape TypeScript: The constructor interface pattern Streaming your Meetup - Part 4: Directing and Streaming with OBS Streaming your Meetup - Part 3: Speaker audio Streaming your Meetup - Part 2: Speaker video Streaming your Meetup - Part 1: Basics and Projector TypeScript and React Guide: Added a new styles chapter TypeScript and React Guide: Added a new render props chapter TypeScript and React: Styles and CSS TypeScript and React TypeScript and React Guide: Added a new prop types chapter TypeScript without TypeScript -- JSDoc superpowers TypeScript: Mapped types for type maps JAMStack vs serverless web apps The Unsung Benefits of JAMStack Sites TypeScript: Ambient modules for Webpack loaders My most favourite talks in 2018 TypeScript and React Guide: Added a new context chapter TypeScript: Built-in generic types TypeScript: Type predicates JSX is syntactic sugar TypeScript and React Guide: Added a new hooks chapter Getting your CfP application right FAQ on our Angular Connect Talk: Automating UI development TypeScript and Substitutability Debugging Node.js apps in TypeScript with Visual Studio Code From Medium: Deconfusing Pre- and Post-processing From Medium: PostCSS misconceptions Saving and scraping a website with Puppeteer Cutting the mustard - 2018 edition Wordpress as CMS for your JAMStack sites My most favourite podcast episodes in 2017 My most favourite talks in 2017 My most favourite books in 2017 The Best Request Is No Request, Revisited Not so hidden figures - Organizing ScriptConf My podcast journey to ScriptCast Grid layout, grid layout everywhere! #scriptconf and #devone Object streams in Node.js
TypeScript: Iterating over objects
2022-05-11 · via oida.dev | TypeScript, Rust

There is rarely a head-scratcher in TypeScript as prominent as trying to access an object property via iterating through its keys. This is a pattern that’s so common in JavaScript, yet TypeScript seems to through all the obstacles at you. This simple line:

Object.keys(person).map(k => person[k])

has TypeScript throwing red squigglies at you and developers flipping tables. It’s just not fun. There are several solutions to that. I tried to “improve” Object.keys once. It’s a nice exercise on declaration merging but uh… I wouldn’t do that too often. Also Dan writes profoundly about this. Annotating definitely is one solution.

But hey, let’s look at the problem first.

Why iterating over objects isn’t so easy #

Let’s take a look at this function:

type Person = {
name: string,
age: number
}

function printPerson(p: Person) {
Object.keys(p).forEach((k) => {
console.log(k, p[k]) // ERROR!!
})
}

All we want is to print a Person’s fields by accessing them through their keys. TypeScript won’t allow this. Object.keys(p) returns a string[], which is too wide to allow accessing a very defined object shape Person.

But why is that so? Isn’t it obvious that we only access keys that are available? That’s the whole point of using Object.keys!

Sure, but we are also able to pass objects that are subtypes of Person, which have more properties than defined in Person.

const me = {
name: "Stefan",
age: 40,
website: "https://oida.dev"
}

printPerson(me); // All good!

So, you might tell me that still printPerson should work correctly. It prints more properties, ok, but it doesn’t break the code. It’s still the keys of p, so every property should be accessible.

Sure, but what if you don’t access p?

So, let’s assume Object.keys gives you (keyof Person)[]. Just like my 2 year old “fix” tries to do. You can easily write something like this:

function printPerson(p: Person) {
const you: Person = {
name: "Reader",
age: NaN
};

Object.keys(p).forEach((k) => {
console.log(k, you[k])
})
}

const me = {
name: "Stefan",
age: 40,
website: "https://oida.dev"
}

printPerson(me);

If Object.keys(p) returns an array of type keyof Person[], you will be able to access other objects of Person, too. This might not add up. In our example, we just print undefined. But what if you try to do something with those values. This will break at runtime.

TypeScript prevents you from scenarios like this. It’s honest and says: Well, you think it might be keyof Person, but in reality, it can be so much more.

Only type guards can help you:

function isKey<T>(x: T, k: PropertyKey): k is keyof T {
return k in x
}

function printPerson(p: Person) {
Object.keys(p).forEach((k) => {
if(isKey(p, k)) console.log(k, p[k]) // All fine!
})
}

But… not so nice, isn’t it?

for-in loops #

There’s another way to iterate over objects:

function printPerson(p: Person) {
for (let k in p) {
console.log(k, p[k]) // Error
}
}

TypeScript gives you the same error: Element implicitly has an ‘any’ type because expression of type ‘string’ can’t be used to index type ‘Person’. For the same reason. You still can do something like this:

function printPerson(p: Person) {
const you: Person = {
name: "Reader",
age: NaN
};

for (let k in p) {
console.log(k, you[k])
}
}

const me = {
name: "Stefan",
age: 40,
website: "https://oida.dev"
}

printPerson(me);

And it will explode at runtime.

However, writing it like this gives you a little edge over the Object.keys version. TypeScript can be much more exact in this scenario if you add a generics:

function printPerson<T extends Person>(p: T) {
for (let k in p) {
console.log(k, p[k]) // This works
}
}

Instead of requiring p to be Person (and thus be compatible with all sub-types of Person), we add a new generic type parameter T that extends from Person. This means that all types that have been compatible to this function signature are still compatible, but the moment we use p, we are dealing with an explicit sub-type, not the broader super-type Person.

We substitute T for something that is compatible with Person, but where TypeScript knows that it’s different enough to prevent you from errors.

The code above works. k is of type keyof T. That’s why we can access p, which is of type T. T being a sub-type of Person, that’s just coincidence.

But we won’t be able to do stuff that might break, like this:

function printPerson<T extends Person>(p: T) {
const you: Person = {
name: "Reader",
age: NaN
}
for (let k in p) {
console.log(k, you[k]) // ERROR
}
}

We can’t access a Person with keyof T. They might be different. Beautiful!

And since T is a sub-type of Person, we still can assign properties:

p.age = you.age

Great!

Bottom line #

TypeScript being very conservative about its types here is something that might seem odd at first but helps you in scenarios you wouldn’t think of. I guess this is the part where JavaScript developers usually scream at the compiler and think they’re “fighting” it, but hey, maybe TypeScript saved your butt. For situations where this gets annoying, TypeScript at least gives you ways to workaround.

Related Articles