Browse Source

TypeScript

master
MYW 2 years ago
parent
commit
d79eac9e21
  1. 13
      TypeScript/01.TS常用类型/16.typeof运算符.ts
  2. 44
      TypeScript/02.TS高级类型/02.类型兼容性/02.函数类型兼容性(参数个数和类型).ts
  3. 20
      TypeScript/02.TS高级类型/02.类型兼容性/03.函数类型兼容性(返回值类型).ts
  4. 21
      TypeScript/02.TS高级类型/02.类型兼容性/04.交叉类型.ts
  5. 17
      TypeScript/02.TS高级类型/03.泛型/01.泛型.ts
  6. 32
      TypeScript/02.TS高级类型/03.泛型/02.泛型约束.ts
  7. 16
      TypeScript/02.TS高级类型/03.泛型/03.多个泛型变量情况.ts
  8. 14
      TypeScript/02.TS高级类型/03.泛型/04.泛型接口.ts
  9. 24
      TypeScript/02.TS高级类型/03.泛型/05.泛型类.ts
  10. 18
      TypeScript/02.TS高级类型/03.泛型/06.泛型工具类型(Partial).ts
  11. 17
      TypeScript/02.TS高级类型/03.泛型/07.泛型工具类型(Readonly).ts
  12. 10
      TypeScript/02.TS高级类型/03.泛型/08.泛型工具类型(Pick).ts
  13. 16
      TypeScript/02.TS高级类型/03.泛型/09.泛型工具类型(Record).ts
  14. 21
      TypeScript/02.TS高级类型/04.索引签名类型.ts
  15. 20
      TypeScript/02.TS高级类型/05.映射类型.ts
  16. 21
      TypeScript/02.TS高级类型/06.索引查询类型.ts
  17. 26
      TypeScript/03.React中使用TS/01.函数组件的类型.tsx
  18. 27
      TypeScript/03.React中使用TS/02.函数默认值.tsx
  19. 37
      TypeScript/03.React中使用TS/03.事件绑定和事件对象.tsx
  20. 23
      TypeScript/03.React中使用TS/04.组件类型.tsx
  21. 27
      TypeScript/03.React中使用TS/05.组件的属性.tsx
  22. 33
      TypeScript/03.React中使用TS/06.组件状态和事件.tsx
  23. 23
      TypeScript/04.任务列表案例/todo_ts/.gitignore
  24. 46
      TypeScript/04.任务列表案例/todo_ts/README.md
  25. 28310
      TypeScript/04.任务列表案例/todo_ts/package-lock.json
  26. 43
      TypeScript/04.任务列表案例/todo_ts/package.json
  27. BIN
      TypeScript/04.任务列表案例/todo_ts/public/favicon.ico
  28. 43
      TypeScript/04.任务列表案例/todo_ts/public/index.html
  29. BIN
      TypeScript/04.任务列表案例/todo_ts/public/logo192.png
  30. BIN
      TypeScript/04.任务列表案例/todo_ts/public/logo512.png
  31. 25
      TypeScript/04.任务列表案例/todo_ts/public/manifest.json
  32. 3
      TypeScript/04.任务列表案例/todo_ts/public/robots.txt
  33. 2
      TypeScript/04.任务列表案例/todo_ts/src/App.css
  34. 69
      TypeScript/04.任务列表案例/todo_ts/src/App.tsx
  35. 63
      TypeScript/04.任务列表案例/todo_ts/src/components/TodoAdd.tsx
  36. 29
      TypeScript/04.任务列表案例/todo_ts/src/components/TodoFooter.tsx
  37. 35
      TypeScript/04.任务列表案例/todo_ts/src/components/TodoList.tsx
  38. 141
      TypeScript/04.任务列表案例/todo_ts/src/css/todos-base.css
  39. 379
      TypeScript/04.任务列表案例/todo_ts/src/css/todos-index.css
  40. 10
      TypeScript/04.任务列表案例/todo_ts/src/index.tsx
  41. 1
      TypeScript/04.任务列表案例/todo_ts/src/logo.svg
  42. 1
      TypeScript/04.任务列表案例/todo_ts/src/react-app-env.d.ts
  43. 15
      TypeScript/04.任务列表案例/todo_ts/src/reportWebVitals.ts
  44. 5
      TypeScript/04.任务列表案例/todo_ts/src/setupTests.ts
  45. 8
      TypeScript/04.任务列表案例/todo_ts/src/todos.d.ts
  46. 26
      TypeScript/04.任务列表案例/todo_ts/tsconfig.json

13
TypeScript/01.TS常用类型/16.typeof运算符.ts

@ -4,7 +4,20 @@
// TS 中的 typeof
let p = { x: 1, y: 2 }
// function formatPoint(point: {x: number; y: number }) {}
<<<<<<< HEAD
// 使用 typeof
=======
<<<<<<< HEAD
// 使用 typeof
=======
<<<<<<< HEAD
// 使用 typeof
=======
// 使用 typeof,拿到对象 p 中的类型,实现简化书写的目的
>>>>>>> ceebe04 (TypeScript)
>>>>>>> 609bdbe (TypeScript)
>>>>>>> a25358e (TypeScript)
function formatPoint(point: typeof p ) {}
formatPoint({ x: 1, y: 100 })

44
TypeScript/02.TS高级类型/02.类型兼容性/02.函数类型兼容性(参数个数和类型).ts

@ -0,0 +1,44 @@
// 1.参数个数:参数少的可以赋值给参数多的
type F1 = (a: number) => void
type F2 = (a: number, b: number) => void
let f1: F1
let f2: F2
// 参数少的 f1 可以赋值给参数多的 f2 。 例: f2 = f1
// 但是参数多的不能赋值给参数少的
// 2.参数类型:相同位置的参数类型要相同兼容
// 参数类型是原始类型
type F3 = (a: number) => void
type F4 = (a: number) => void
let f3: F3
let f4: F4
// 相同位置和相同类型的参数,可以参数少的可以赋值给参数多的,也可以参数多的赋值给参数少的。 例:f3 = f4 f4 = f3
// 参数类型是对象类型
interface Point2D {
x: number
y: number
}
interface Point3D {
x: number
y: number
z: number
}
type F5 = (p: Point2D) => void // 相当于有两个参数
type F6 = (p: Point3D) => void // 相当于有三个参数
let f5: F5
let f6: F6
// 参数少的可以赋值给参数多的,但是参数多的不可以赋值给参数少的

20
TypeScript/02.TS高级类型/02.类型兼容性/03.函数类型兼容性(返回值类型).ts

@ -0,0 +1,20 @@
// 返回值类型:只关注返回值类型本身
// 返回值类型是原始类型:
type F5 = () => string
type F6 = () => string
let f5: F5
let f6: F6
// 返回值类型一样,f5 和 f6 可以相互赋值。 例: f6 = f5 f5 = f6
// 返回值类型是对象类型:
type F7 = () => { name: string }
type F8 = () => { name: string; age: number }
let f7: F7
let f8: F8
// 成员多的赋值给成员少的 f7 = f8

21
TypeScript/02.TS高级类型/02.类型兼容性/04.交叉类型.ts

@ -0,0 +1,21 @@
// 交叉类型:功能类似 extends,用于组合多个类型为一个类型(常用于对象类型)
interface Person {
name: string
say(): number
}
interface Contact {
phone: string
}
// PersonDetail 同时具有 Person 和 Contact 的属性和方法
type PersonDetail = Person & Contact
let obj: PersonDetail = {
name: 'jack',
phone: '123456',
say() {
return 1
}
}

17
TypeScript/02.TS高级类型/03.泛型/01.泛型.ts

@ -0,0 +1,17 @@
// 使用泛型创建一个函数
function id<Type>(value: Type) : Type { // <Type> 相当于是一个占位符
return value
}
// 调用泛型函数:
// 1.以 number 类型调用泛型函数
const num = id<number>(10)
// 2.以 string 类型调用泛型函数
const str = id<string>('a')
const ret = id<boolean>(false)
// 泛型简写
let num1 = id(100) // number类型
let str1 = id('abc') // string类型

32
TypeScript/02.TS高级类型/03.泛型/02.泛型约束.ts

@ -0,0 +1,32 @@
// 泛型约束的两种方式:1.指定更加具体的类型 2.添加约束
function id<Type>(value: Type) : Type {
// Type 是表示任意类型的,导致 value 没办法访问任何属性和方法
// console.log(value.length) // length 会报错
return value
}
// 第一种:指定更加具体的类型
// 将类型修改为 Type[] ( Type 类型的数组 ),因为只要是数组就一定存在 length 属性,所以可以访问
function id2<Type>(value: Type[]) : Type[] {
console.log(value.length)
return value
}
// 第二种:添加约束
interface ILength { length: number }
// 这里的 extends 不是继承,而是表示 Type 要满足 ILength 中 length 属性的约束
function id3 <Type extends ILength> (value: Type) : Type {
value.length
return value
}
// 只要满足 length 属性就可以
id3(['a', 'x'])
id3('abc')
id3({length: 10, name: 'jack'})
// 错误演示
// id3(123)

16
TypeScript/02.TS高级类型/03.泛型/03.多个泛型变量情况.ts

@ -0,0 +1,16 @@
// 第二个类型变量受第一个类型变量约束
// Key 受 Type 约束(Key 要满足 Type 中的属性)
function getProp<Type, Key extends keyof Type>(obj: Type, key: Key) {
return obj[key]
}
getProp({ name: 'jack', age: 20}, 'age') // 第二个参数要满足第一个参数的属性,可以是 age 或 name
getProp({ name: 'jack', age: 20}, 'name')
// 补充
getProp(20, 'toFixed') // 20 是 number 类型,toFixed 是 number 类型能够访问的方法
getProp('abc', 'split')
getProp('abc', 1) // 1表示索引,字符串像一个数组一样,可以通过数组的索引的方式来访问
getProp(['a'], length)
getProp(['a'], 1000)

14
TypeScript/02.TS高级类型/03.泛型/04.泛型接口.ts

@ -0,0 +1,14 @@
interface IdFunc<Type> {
id: (balue: Type) => Type
ids: () => Type[]
}
// 需要指定显示类型
let obj: IdFunc<number> = { // 指定显示的类型为 number
id(value) {
return value
},
ids() {
return [1, 3, 5]
}
}

24
TypeScript/02.TS高级类型/03.泛型/05.泛型类.ts

@ -0,0 +1,24 @@
// 第一种
class GenericNumber<NumType> {
defaultValue: NumType
add: (x: NumType, y: NumType) => NumType
}
// 明确指定 <类型>
const myNum = new GenericNumber<number>()
myNum.defaultValue = 10
// 第二种
class GenericNumber2<NumType> {
defaultValue: NumType
add: (x: NumType, y: NumType) => NumType
constructor(value: NumType) {
this.defaultValue = value
}
}
// 省略类型
const myNum2 = new GenericNumber()
myNum.defaultValue = 10

18
TypeScript/02.TS高级类型/03.泛型/06.泛型工具类型(Partial).ts

@ -0,0 +1,18 @@
// 泛型工具类型:Partial<Type> 用来构造(创建)一个类型,将 Type 的所有属性设置为可选
interface Props {
id: string
children: number[]
}
// 使用 泛型工具 Partial
type PartialProps = Partial<Props>
// 没有用泛型工具,属性是必加的
let p1: Props = {
id: '',
children: [1]
}
// 加入 PartialProps 后,属性可加可不加
let p2: PartialProps = {}

17
TypeScript/02.TS高级类型/03.泛型/07.泛型工具类型(Readonly).ts

@ -0,0 +1,17 @@
// 泛型工具类型:Readonly<Type> 将 Type 的所有属性都设置为 readonly(只读)
interface Props {
id: string
children: number[]
}
type ReadonlyProps = Readonly<Props>
// 所有属性都是只读
let p1: ReadonlyProps = {
id: '1',
children: [1, 3]
}
// 属性是只读,不能修改
// p1.id = '2'

10
TypeScript/02.TS高级类型/03.泛型/08.泛型工具类型(Pick).ts

@ -0,0 +1,10 @@
// 泛型工具类型:Pick<Type, Keys> 从 Type 中选择一组属性来构造新类型
interface Props{
id: string
title: string
children: number[]
}
// 从 Props 中选择两个属性给 PickProps
type PickProps = Pick<Props, 'id' | 'title'> // PickProps 具有 Props 的 id 和 title 属性

16
TypeScript/02.TS高级类型/03.泛型/09.泛型工具类型(Record).ts

@ -0,0 +1,16 @@
// 泛型工具类型:Record<Keys, type> 构造一个对象类型,属性键为 Keys, 属性类型为 Type
type RecordObj = Record< 'a' | 'b' | 'c', string[]> // 表示属性键 a、b、c 都是 string 类型的数组
let obj: RecordObj = {
a: ['a'],
b: ['b'],
c: ['c']
}
// 不使用工具类型 Record
type RecordObj2 = {
a2: string[]
b2: string[]
c2: string[]
}

21
TypeScript/02.TS高级类型/04.索引签名类型.ts

@ -0,0 +1,21 @@
// 索引签名类型
// 使用场景:无法确定对象中有哪些属性(或者对象中出现任意多个属性) 例:用户输入的内容
interface AnyObject {
// 字符串的键都可以出现在 AnyObject 对象中
[key: string] : number // key 表示一个占位符(可以更改名字),number 表示属性的值的类型
}
let obj: AnyObject = {
a: 1,
abc: 123,
}
// 在数组中使用
interface MyArray<Type> {
[index: number]: Type
}
let arr1 : MyArray<number> = [1, 3, 5]
arr1[0]

20
TypeScript/02.TS高级类型/05.映射类型.ts

@ -0,0 +1,20 @@
// 映射类型:基于旧类型创建新类型
type PropKeys = 'x' | 'y' | 'z' // 联合类型
type Type1 = { x: number; y: number; z: number }
// 使用映射类型进行简化
type Type2 = { [Key in PropKeys]: number } // key 表示 PropKeys 联合类型中的任意一个
// 映射类型只能在类型别名中使用,不能在接口中使用
// 错误演示:
/*
* interface Type3 {
* [Key in PropKeys]: number
* }
*/
// 根据对象类型创建新类型
type Props = { a: number; b: string; c: boolean }
type Type3 = { [key in keyof Props]: number } // key in 表示 key 可以是 Props 中所有个键名称中的任意一个,类型为 number

21
TypeScript/02.TS高级类型/06.索引查询类型.ts

@ -0,0 +1,21 @@
type Props = { a: number; b: string; c: boolean }
// 查看 Props 中 a 的类型
type TypeA = Props['a']
// 模拟 Partial 类型:
type MyPartial<T> = {
[P in keyof T]?: T[P] // p 相当于 key
}
type PartialProps = MyPartial<Props>
// 索引其它查询方式:同时查询多个索引的类型
type TypeB = Props['a' | 'b']
type Props2 = { a: number; b: number; c: boolean }
type TypeC = Props2['a' | 'b']
// a 和 b 类型都是 number,TypeD 只显示一个 number,实现去重功能
type TypeD = Props2[keyof Props]

26
TypeScript/03.React中使用TS/01.函数组件的类型.tsx

@ -0,0 +1,26 @@
import ReactDOM from "react-dom";
import { FC } from "react";
// React 函数组件的类型以及组件的属性
type Props = { name: string; age?: number}
// 第一种:
const Hello: FC<Props> = ( { name, age } ) => (
<div>{name}, { age } </div>
)
// 第二种(简化):
const HI = ({ name, age }: Props) => (
<div>
{name}, { age }
</div>
)
const App = () => <div>
{/* name 是必填的 */}
<Hello name="jack" age={ 30 }/>
<HI name="Ken" age={ 20 }/>
</div>
ReactDOM.render(<App />, document.getElementById('root'))

27
TypeScript/03.React中使用TS/02.函数默认值.tsx

@ -0,0 +1,27 @@
import ReactDOM from "react-dom";
// 函数的默认值
type Props = { name: string; age?: number}
// const Hello: FC<Props> = ( { name, age } ) => (
// <div>我是:{name}, 我 { age } 岁</div>
// )
// 提供默认属性
// Hello.defaultProps = {
// age: 18 // age 是可选属性,当没给 age 的值时,默认 age 的值为 18
// }
// 简化写法
const Hello = ( { name, age = 18 }: Props ) => ( // 给 age 添加默认值 18
<div>{name}, { age } </div>
)
const App = () => <div>
{/* name 是必填的 */}
<Hello name="jack" />
</div>
ReactDOM.render(<App />, document.getElementById('root'))

37
TypeScript/03.React中使用TS/03.事件绑定和事件对象.tsx

@ -0,0 +1,37 @@
import React from "react";
import ReactDOM from "react-dom";
// 事件绑定和事件对象
type Props = { name: string; age?: number}
const Hello = ( { name, age = 18 }: Props ) => {
// const onClick = () => {
// console.log('赞!')
// }
const onClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log('赞!', e.currentTarget)
}
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value)
}
return (
<div>
{name}, { age }
<button onClick={onClick}></button>
<input onChange={onChange} />
{/* 可以利用 TS 的类型推论来查看事件对象类型(将鼠标放在 e 上可以查看) */}
{/* <input onChange={ e => {} } /> */}
</div>
)
}
const App = () => <div>
{/* name 是必填的 */}
<Hello name="jack" />
</div>
ReactDOM.render(<App />, document.getElementById('root'))

23
TypeScript/03.React中使用TS/04.组件类型.tsx

@ -0,0 +1,23 @@
import React from "react";
import ReactDOM from "react-dom";
// 组件的类型
type State = { count: number}
type Props = { message?: string}
// 无 props, 无 state
class C1 extends React.Component {}
// 有 props,无 state
class C2 extends React.Component<Props> {}
// 无 props,有 state
class C3 extends React.Component<{}, State> {}
// 有 props,有 state
class C4 extends React.Component<Props, State> {}
const App = () => <div></div>
ReactDOM.render(<App />, document.getElementById('root'))

27
TypeScript/03.React中使用TS/05.组件的属性.tsx

@ -0,0 +1,27 @@
import React from "react";
import ReactDOM from "react-dom";
// 组件的属性
type Props = { name: string; age?: number }
class Hello extends React.Component<Props> {
// 在类组件中给 age 添加默认属性
static defaultProps: Partial<Props> = {
age: 20
}
render() {
const { name, age } = this.props
return (
<div>{name}, { age } </div>
)
}
}
const App = () => <div>
<Hello name="Ken"/>
</div>
ReactDOM.render(<App />, document.getElementById('root'))

33
TypeScript/03.React中使用TS/06.组件状态和事件.tsx

@ -0,0 +1,33 @@
import React from "react";
import ReactDOM from "react-dom";
// 组件状态和事件
type State = { count: number }
class Counter extends React.Component<{}, State> {
state: State = {
count: 0
}
handleCLick = () => {
this.setState({
count: this.state.count + 1
})
}
render() {
return (
<div>
: {this.state.count}
<button onClick={this.handleCLick}>+1</button>
</div>
)
}
}
const App = () => <div>
<Counter/>
</div>
ReactDOM.render(<App />, document.getElementById('root'))

23
TypeScript/04.任务列表案例/todo_ts/.gitignore

@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

46
TypeScript/04.任务列表案例/todo_ts/README.md

@ -0,0 +1,46 @@
# Getting Started with Create React App
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.\
You will also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).

28310
TypeScript/04.任务列表案例/todo_ts/package-lock.json
File diff suppressed because it is too large
View File

43
TypeScript/04.任务列表案例/todo_ts/package.json

@ -0,0 +1,43 @@
{
"name": "todo_ts",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.11.47",
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"typescript": "^4.7.4",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

BIN
TypeScript/04.任务列表案例/todo_ts/public/favicon.ico

43
TypeScript/04.任务列表案例/todo_ts/public/index.html

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

BIN
TypeScript/04.任务列表案例/todo_ts/public/logo192.png

After

Width: 192  |  Height: 192  |  Size: 5.2 KiB

BIN
TypeScript/04.任务列表案例/todo_ts/public/logo512.png

After

Width: 512  |  Height: 512  |  Size: 9.4 KiB

25
TypeScript/04.任务列表案例/todo_ts/public/manifest.json

@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

3
TypeScript/04.任务列表案例/todo_ts/public/robots.txt

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

2
TypeScript/04.任务列表案例/todo_ts/src/App.css

@ -0,0 +1,2 @@
@import url('./css/todos-base.css');
@import url('./css/todos-index.css');

69
TypeScript/04.任务列表案例/todo_ts/src/App.tsx

@ -0,0 +1,69 @@
import { Component } from 'react';
import { text } from 'stream/consumers';
import './App.css';
import TodoAdd from './components/TodoAdd'
import TodoFooter from './components/TodoFooter'
import TodoList from './components/TodoList'
// 导入 TodoItem 类型
import { TodoItem } from './todos'
// App 组件的状态类型
type Todos = {
todos: TodoItem[]
}
const todos: TodoItem[] = [
{
id: 1,
text: '吃饭',
done: true
},
{
id: 2,
text: '休息',
done: false
}
]
class App extends Component<{}, Todos> {
state: Todos = {
todos
}
addTodo = (text: string) => {
// console.log('父组件中获取的数据:', text)
const { todos } = this.state
const id = todos.length === 0 ? 1 :todos[ todos.length - 1 ].id + 1
this.setState({
todos: [ ...todos, {
id,
text,
done:false
}]
})
}
render() {
return (
<section className='todoapp'>
{/* 添加任务 */}
<TodoAdd onAddTodo={this.addTodo}/>
<section className='main'>
<input id="toggle-all" className="toggle-all" type="checkbox" />
<label htmlFor="toggle-all">Mark all as complete</label>
{/* 列表组件 */}
<TodoList list={this.state.todos} />
</section>
{/* footer 组件 */}
<TodoFooter/>
</section>
)
}
}
export default App;

63
TypeScript/04.任务列表案例/todo_ts/src/components/TodoAdd.tsx

@ -0,0 +1,63 @@
import React from "react";
// 属性的类型
type Props = {
onAddTodo(text: string): void
}
// 状态的类型
type State = {
text: string
}
class TodoAdd extends React.Component<Props, State> {
state: State = {
text: ''
}
onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
this.setState({
text: e.target.value
})
}
onAdd = (e: React.KeyboardEvent<HTMLInputElement>) => {
// 非空判断
const { text } = this.state
if (text.trim() === '') return
// console.log(e.keyCode)
// 注意:keyCode 属性将来会被弃用,因此,在 TS 中使用的时候,会有一个中横线
if (e.keyCode === 13) {
this.props.onAddTodo(this.state.text)
// 清空文本框的值
this.setState({
text: ''
})
}
// 也可以使用 code 属性来代替
// console.log(e.code)
// if(e.code === 'Enter') {
// console.log('enter')
// }
}
render() {
return (
<header className="header">
<h1>todos</h1>
<input
className="new-todo"
placeholder="请输入内容"
autoFocus
value={this.state.text}
onChange={this.onChange}
onKeyDown={this.onAdd}
/>
</header>
)
}
}
export default TodoAdd

29
TypeScript/04.任务列表案例/todo_ts/src/components/TodoFooter.tsx

@ -0,0 +1,29 @@
import React from 'react'
class TodoFooter extends React.Component {
render() {
return (
<footer className="footer">
<span className="todo-count">
<strong>0</strong> item left
</span>
<ul className="filters">
<li>
<a className="selected" href="#/">
All
</a>
</li>
<li>
<a href="#/active">Active</a>
</li>
<li>
<a href="#/completed">Completed</a>
</li>
</ul>
<button className="clear-completed">Clear completed</button>
</footer>
)
}
}
export default TodoFooter

35
TypeScript/04.任务列表案例/todo_ts/src/components/TodoList.tsx

@ -0,0 +1,35 @@
import React from "react";
// 导入 TodoItem 类型
import { TodoItem } from '../todos'
// 提供 props 类型
interface Props {
list: TodoItem[]
}
class TodoList extends React.Component<Props> {
render() {
console.log(this.props)
return (
<ul className="todo-list">
{ this.props.list.map(todo => {
// 编辑样式:editing 已完成样式:completed
return <li key={todo.id} className={ todo.done ? 'completed' : '' }>
<div className="view">
<input className="toggle" type="checkbox" />
<label>{todo.text}</label>
<button className="destroy"></button>
</div>
<input className="edit" defaultValue="Create a TodoMVC template" />
</li>
})}
</ul>
)
}
}
export default TodoList

141
TypeScript/04.任务列表案例/todo_ts/src/css/todos-base.css

@ -0,0 +1,141 @@
hr {
margin: 20px 0;
border: 0;
border-top: 1px dashed #c5c5c5;
border-bottom: 1px dashed #f7f7f7;
}
.learn a {
font-weight: normal;
text-decoration: none;
color: #b83f45;
}
.learn a:hover {
text-decoration: underline;
color: #787e7e;
}
.learn h3,
.learn h4,
.learn h5 {
margin: 10px 0;
font-weight: 500;
line-height: 1.2;
color: #000;
}
.learn h3 {
font-size: 24px;
}
.learn h4 {
font-size: 18px;
}
.learn h5 {
margin-bottom: 0;
font-size: 14px;
}
.learn ul {
padding: 0;
margin: 0 0 30px 25px;
}
.learn li {
line-height: 20px;
}
.learn p {
font-size: 15px;
font-weight: 300;
line-height: 1.3;
margin-top: 0;
margin-bottom: 0;
}
#issue-count {
display: none;
}
.quote {
border: none;
margin: 20px 0 60px 0;
}
.quote p {
font-style: italic;
}
.quote p:before {
content: '“';
font-size: 50px;
opacity: .15;
position: absolute;
top: -20px;
left: 3px;
}
.quote p:after {
content: '”';
font-size: 50px;
opacity: .15;
position: absolute;
bottom: -42px;
right: 3px;
}
.quote footer {
position: absolute;
bottom: -40px;
right: 0;
}
.quote footer img {
border-radius: 3px;
}
.quote footer a {
margin-left: 5px;
vertical-align: middle;
}
.speech-bubble {
position: relative;
padding: 10px;
background: rgba(0, 0, 0, .04);
border-radius: 5px;
}
.speech-bubble:after {
content: '';
position: absolute;
top: 100%;
right: 30px;
border: 13px solid transparent;
border-top-color: rgba(0, 0, 0, .04);
}
.learn-bar > .learn {
position: absolute;
width: 272px;
top: 8px;
left: -300px;
padding: 10px;
border-radius: 5px;
background-color: rgba(255, 255, 255, .6);
transition-property: left;
transition-duration: 500ms;
}
@media (min-width: 899px) {
.learn-bar {
width: auto;
padding-left: 300px;
}
.learn-bar > .learn {
left: 8px;
}
}

379
TypeScript/04.任务列表案例/todo_ts/src/css/todos-index.css

@ -0,0 +1,379 @@
html,
body {
margin: 0;
padding: 0;
}
button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
font-weight: inherit;
color: inherit;
-webkit-appearance: none;
appearance: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
background: #f5f5f5;
color: #4d4d4d;
min-width: 230px;
max-width: 550px;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-weight: 300;
}
:focus {
outline: 0;
}
.hidden {
display: none;
}
.todoapp {
background: #fff;
margin: 130px 0 40px 0;
position: relative;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.todoapp input::-webkit-input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
.todoapp input::-moz-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
.todoapp input::input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
.todoapp h1 {
position: absolute;
top: -155px;
width: 100%;
font-size: 100px;
font-weight: 100;
text-align: center;
color: rgba(175, 47, 47, 0.15);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
.new-todo,
.edit {
position: relative;
margin: 0;
width: 100%;
font-size: 24px;
font-family: inherit;
font-weight: inherit;
line-height: 1.4em;
border: 0;
color: inherit;
padding: 6px;
border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.new-todo {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.003);
box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
}
.main {
position: relative;
z-index: 2;
border-top: 1px solid #e6e6e6;
}
.toggle-all {
width: 1px;
height: 1px;
border: none; /* Mobile Safari */
opacity: 0;
position: absolute;
right: 100%;
bottom: 100%;
}
.toggle-all + label {
width: 60px;
height: 34px;
font-size: 0;
position: absolute;
top: -52px;
left: -13px;
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
}
.toggle-all + label:before {
content: '❯';
font-size: 22px;
color: #e6e6e6;
padding: 10px 27px 10px 27px;
}
.toggle-all:checked + label:before {
color: #737373;
}
.todo-list {
margin: 0;
padding: 0;
list-style: none;
}
.todo-list li {
position: relative;
font-size: 24px;
border-bottom: 1px solid #ededed;
}
.todo-list li:last-child {
border-bottom: none;
}
.todo-list li.editing {
border-bottom: none;
padding: 0;
}
.todo-list li.editing .edit {
display: block;
width: 506px;
padding: 12px 16px;
margin: 0 0 0 43px;
}
.todo-list li.editing .view {
display: none;
}
.todo-list li .toggle {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
height: auto;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
border: none; /* Mobile Safari */
-webkit-appearance: none;
appearance: none;
}
.todo-list li .toggle {
opacity: 0;
}
.todo-list li .toggle + label {
/*
Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433
IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/
*/
background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E');
background-repeat: no-repeat;
background-position: center left;
}
.todo-list li .toggle:checked + label {
background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E');
}
.todo-list li label {
word-break: break-all;
padding: 15px 15px 15px 60px;
display: block;
line-height: 1.2;
transition: color 0.4s;
}
.todo-list li.completed label {
color: #d9d9d9;
text-decoration: line-through;
}
.todo-list li .destroy {
display: none;
position: absolute;
top: 0;
right: 10px;
bottom: 0;
width: 40px;
height: 40px;
margin: auto 0;
font-size: 30px;
color: #cc9a9a;
margin-bottom: 11px;
transition: color 0.2s ease-out;
}
.todo-list li .destroy:hover {
color: #af5b5e;
}
.todo-list li .destroy:after {
content: '×';
}
.todo-list li:hover .destroy {
display: block;
}
.todo-list li .edit {
display: none;
}
.todo-list li.editing:last-child {
margin-bottom: -1px;
}
.footer {
color: #777;
padding: 10px 15px;
height: 20px;
text-align: center;
border-top: 1px solid #e6e6e6;
}
.footer:before {
content: '';
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 50px;
overflow: hidden;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
0 8px 0 -3px #f6f6f6,
0 9px 1px -3px rgba(0, 0, 0, 0.2),
0 16px 0 -6px #f6f6f6,
0 17px 2px -6px rgba(0, 0, 0, 0.2);
}
.todo-count {
float: left;
text-align: left;
}
.todo-count strong {
font-weight: 300;
}
.filters {
margin: 0;
padding: 0;
list-style: none;
position: absolute;
right: 0;
left: 0;
}
.filters li {
display: inline;
}
.filters li a {
color: inherit;
margin: 3px;
padding: 3px 7px;
text-decoration: none;
border: 1px solid transparent;
border-radius: 3px;
}
.filters li a:hover {
border-color: rgba(175, 47, 47, 0.1);
}
.filters li a.selected {
border-color: rgba(175, 47, 47, 0.2);
}
.clear-completed,
html .clear-completed:active {
float: right;
position: relative;
line-height: 20px;
text-decoration: none;
cursor: pointer;
}
.clear-completed:hover {
text-decoration: underline;
}
.info {
margin: 65px auto 0;
color: #bfbfbf;
font-size: 10px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
text-align: center;
}
.info p {
line-height: 1;
}
.info a {
color: inherit;
text-decoration: none;
font-weight: 400;
}
.info a:hover {
text-decoration: underline;
}
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox
*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
.toggle-all,
.todo-list li .toggle {
background: none;
}
.todo-list li .toggle {
height: 40px;
}
}
@media (max-width: 430px) {
.footer {
height: 50px;
}
.filters {
bottom: 10px;
}
}

10
TypeScript/04.任务列表案例/todo_ts/src/index.tsx

@ -0,0 +1,10 @@
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<App />
);

1
TypeScript/04.任务列表案例/todo_ts/src/logo.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

1
TypeScript/04.任务列表案例/todo_ts/src/react-app-env.d.ts

@ -0,0 +1 @@
/// <reference types="react-scripts" />

15
TypeScript/04.任务列表案例/todo_ts/src/reportWebVitals.ts

@ -0,0 +1,15 @@
import { ReportHandler } from 'web-vitals';
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

5
TypeScript/04.任务列表案例/todo_ts/src/setupTests.ts

@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

8
TypeScript/04.任务列表案例/todo_ts/src/todos.d.ts

@ -0,0 +1,8 @@
// 类型声明文件
// 任务项的类型
export type TodoItem = {
id: number
text: string
done: boolean
}

26
TypeScript/04.任务列表案例/todo_ts/tsconfig.json

@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"src"
]
}
Loading…
Cancel
Save